--- a +++ b/Code/All PennyLane QML Demos/23 Unitary Designs 0.99 Fidelity kkawchak.ipynb @@ -0,0 +1,1018 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "_mDT6fzqwQlp" + }, + "outputs": [], + "source": [ + "# This cell is added by sphinx-gallery\n", + "# It can be customized to whatever you like\n", + "%matplotlib inline\n", + "# !pip install pennylane" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HmioP9IlwQlq" + }, + "source": [ + "Unitary designs\n", + "===============\n", + "\n", + "::: {.meta}\n", + ":property=\\\"og:description\\\": Learn about designs and their uses in\n", + "quantum computing.\n", + "\n", + ":property=\\\"og:image\\\": <https://pennylane.ai/qml/_images/fano.png>\n", + ":::\n", + "\n", + "::: {.related}\n", + "tutorial\\_haar\\_measure Understanding the Haar measure\n", + ":::\n", + "\n", + "*Author: Olivia Di Matteo --- Posted: 07 September 2021. Last updated:\n", + "07 September 2021.*\n", + "\n", + "::: {.note}\n", + "::: {.title}\n", + "Note\n", + ":::\n", + "\n", + "This demo is intended to be a sequel to the\n", + "`demo about the Haar measure </demos/tutorial_haar_measure>`{.interpreted-text\n", + "role=\"doc\"}. If you are not familiar with the Haar measure, we recommend\n", + "going through that demo first before exploring this one.\n", + ":::\n", + "\n", + "Take a close look at the following mathematical object:\n", + "\n", + "{.align-center\n", + "width=\"30.0%\"}\n", + "\n", + "|\n", + "\n", + "There are many things we can say about it: it consists of seven points\n", + "and seven lines (the circle counts as a line); each line contains three\n", + "points, and each point is contained in three lines. Furthermore, any\n", + "pair of points occur together in exactly one line. This object, called\n", + "the [Fano plane](https://en.wikipedia.org/wiki/Fano_plane), is an\n", + "instance of a mathematical structure called a [projective\n", + "plane](https://en.wikipedia.org/wiki/Projective_plane), which is just\n", + "one example of a [combinatorial\n", + "design](https://en.wikipedia.org/wiki/Combinatorial_design). Designs are\n", + "sets of objects, and groups of those objects, that satisfy certain\n", + "balance properties and symmetries. They have been studied for hundreds\n", + "of years in a huge variety of contexts, from [error correcting\n", + "codes](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.50.5465),\n", + "to [card\n", + "games](https://homepages.warwick.ac.uk/staff/D.Maclagan/papers/set.pdf),\n", + "and even\n", + "[agriculture](http://www-groups.mcs.st-and.ac.uk/~rab/histLShand.pdf).\n", + "So, what about quantum computing?\n", + "\n", + "Designs are actually quite prevalent in quantum computing. You\\'ve\n", + "almost certainly come across one before, though you may not have\n", + "realized it. At the end of the Haar measure demo, we asked a very\n", + "important question: \\\"do we always *need* to sample from the full Haar\n", + "measure?\\\". The answer to this is \\\"no\\\", and the reasoning lies in the\n", + "study of *unitary designs*.\n", + "\n", + "In this demo, you\\'ll learn the definition of $t$-designs, what it means\n", + "to generalize them to unitary $t$-designs, and you\\'ll see some\n", + "canonical examples of designs in quantum computing. You\\'ll also learn\n", + "about their connection with the Haar measure, what it means to *twirl* a\n", + "quantum channel, and explore how to leverage 2-designs in PennyLane to\n", + "compute the average fidelity of a noisy channel. You will experience\n", + "directly a situation where we can use a $t$-design as a shortcut over\n", + "the full Haar measure to greatly improve the efficiency of a task 🎉.\n", + "\n", + "From spheres to unitary $t$-designs\n", + "-----------------------------------\n", + "\n", + "### Spherical designs\n", + "\n", + "Before diving into unitary designs, let\\'s look at the sphere for some\n", + "intuition. Suppose we have a polynomial in $d$ variables, and we would\n", + "like to compute its average over the surface of a real, $d$-dimensional\n", + "unit sphere, $S(R^d)$. We can do so by integrating that function over\n", + "the sphere (using the proper measure), but that would be a lot of\n", + "parameters to keep track of.\n", + "\n", + "One could alternatively approximate the average by sampling thousands of\n", + "points uniformly at random on the sphere, evaluating the function at\n", + "those points, and computing their average value. That will always work,\n", + "and it will get us close, but it will not be exact.\n", + "\n", + "In fact, both of those approaches may be overkill in some special\n", + "cases---if the terms in the polynomial have the same degree of at most\n", + "$t$, you can compute the average **exactly** over the sphere using only\n", + "a small set of points rather than integrating over the entire sphere.\n", + "That set of points is called a spherical $t$-design. More formally,:\n", + "\n", + "::: {.admonition .defn}\n", + "Definition\n", + "\n", + "Let $p_t: \\mathcal{S}(R^d)\\rightarrow R$ be a polynomial in $d$\n", + "variables, with all terms homogeneous in degree at most $t$. A set\n", + "$X = \\{x: x \\in \\mathcal{S}(R^d)\\}$ is a spherical $t$-design if\n", + "\n", + "$$\\frac{1}{|X|} \\sum_{x \\in X} p_t(x) = \\int_{\\mathcal{S}(R^d)} p_t (u) d\\mu(u)$$\n", + "\n", + "holds for all possible $p_t$, where $d\\mu$ is the uniform, normalized\n", + "spherical measure. A spherical $t$-design is also a $k$-design for all\n", + "$k < t$.\n", + ":::\n", + "\n", + "Now this is a pretty abstract picture, so let\\'s consider the\n", + "3-dimensional sphere. This definition tells us that if we want to take\n", + "the average of a polynomial over a sphere where all terms have the same\n", + "degree of at most 2, we can do so using a small, representative set of\n", + "points called a 2-design, rather than the whole sphere. Similarly, if\n", + "all terms of the polynomial have the same degree of at most 3, we could\n", + "use a 3-design.\n", + "\n", + "But what are these representative sets of points? Since we are using\n", + "these points as a stand-in for averaging over the whole sphere, we\\'d\n", + "want the points in the set to be distributed in a way that provides\n", + "sufficient \\\"coverage\\\". In the 3-dimensional case, the vertices of some\n", + "familiar solids form $t$-designs ,:\n", + "\n", + "{.align-center\n", + "width=\"80.0%\"}\n", + "\n", + "|\n", + "\n", + "We see from these illustrations that spherical designs are sets of\n", + "*evenly-spaced points*. As $t$ increases, the configurations become\n", + "increasingly sphere-like. Looking at this in a different way, the more\n", + "complex a function becomes as its degree increases, the closer the\n", + "$t$-design must be to a sphere; we need to evaluate the function at more\n", + "points in order to gain sufficient information when a function is\n", + "varying more quickly due to a higher degree. In 3 dimensions, we can\n", + "compute the average of a polynomial with degree 2 by evaluating it only\n", + "at the points of a tetrahedron, despite the fact that it doesn\\'t look\n", + "spherical at all. More complex functions require more points and thus\n", + "more intricate configurations for the design. Spherical designs exist\n", + "for all $t$ and dimension $d$. They are not always unique, and may have\n", + "varying numbers of points.\n", + "\n", + "To show that this really works, let\\'s look at an explicit example.\n", + "Consider the following polynomial in 3 variables:\n", + "\n", + "$$f(x, y, z) = x^4 - 4 x^3 y + y^2 z^2$$\n", + "\n", + "We can compute the average value of $f$ by integrating over a unit\n", + "sphere: the result is $4/15 \\approx 0.26667$. However, this integral is\n", + "non-trivial to evaluate by hand; the most straightforward way is to\n", + "convert to polar coordinates, and even then, it involves integrating\n", + "functions with 4th and 5th powers of trigonometric functions.\n", + "\n", + "Instead, this is a case where we can leverage the fact that all terms in\n", + "the polynomial have degree 4, and compute the average exactly using only\n", + "a subset of points that form a 4-design. We choose a dodecahedron for\n", + "convenience; while this is actually a 5 design, it also forms a\n", + "4-design, and is a more familiar shape than the 4-design depicted above.\n", + "\n", + "First, we define the set of points that comprise a dodecahedron:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "g_cdAioPwQlu" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "# The golden ratio\n", + "g = (1 + np.sqrt(5)) / 2\n", + "\n", + "# A dodecahedron has 20 points\n", + "dodecahedron = np.array([\n", + " # 8 of them form a cube within the sphere\n", + " [1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1],\n", + " [-1, 1, 1], [-1, 1, -1], [-1, -1, 1], [-1, -1, -1],\n", + "\n", + " # 4 of them form a rectangle within the y-z plane\n", + " [0, g, 1/g], [0, g, -1/g], [0, -g, 1/g], [0, -g, -1/g],\n", + "\n", + " # 4 of them form a rectangle within the x-z plane\n", + " [1/g, 0, g], [1/g, 0, -g], [-1/g, 0, g], [-1/g, 0, -g],\n", + "\n", + " # 4 of them form a rectangle within the x-y plane\n", + " [g, 1/g, 0],[g, -1/g, 0], [-g, 1/g, 0], [-g, -1/g, 0],\n", + "])\n", + "\n", + "# Normalize the points so they all fit in the unit sphere\n", + "dodecahedron = np.array(\n", + " [point / np.linalg.norm(point) for point in dodecahedron]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PBMbC8iewQlv" + }, + "source": [ + "Now we define our function and compute the average over the\n", + "dodecahedron:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "ErTDyta4wQlv", + "outputId": "5a744abd-d42e-44b8-b0b8-ca0178808636" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.2666666666666668\n" + ] + } + ], + "source": [ + "def f(x, y, z):\n", + " return (x ** 4) - 4 * (x ** 3) * y + y ** 2 * z ** 2\n", + "\n", + "dodeca_average = np.mean([f(*point) for point in dodecahedron])\n", + "print(dodeca_average)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "czP6zKXlwQlw" + }, + "source": [ + "This is exactly the value we expect. What happens if we try to do this\n", + "using only a 3-design, the cube?\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "vlX-usmgwQlw", + "outputId": "2055d731-9daa-47ca-a8ec-a7681a86ef9e" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.22222222222222235\n" + ] + } + ], + "source": [ + "# The first 8 points of the dodecahedron are a cube\n", + "cube = dodecahedron[:8]\n", + "\n", + "cube_average = np.mean([f(*point) for point in cube])\n", + "print(cube_average)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lHNtu3-GwQlx" + }, + "source": [ + "This clearly differs from the true value. We need a design with $t=4$ or\n", + "better in order to compute this average, and when such a design is\n", + "available, we may save significant computational time.\n", + "\n", + "Unitary designs\n", + "===============\n", + "\n", + "We\\'ve learned now that spherical designs are sets of evenly-spaced\n", + "points, and saw how they can be used as a shortcut to evaluate the\n", + "average of a polynomial up to a given degree $t$. However, there was\n", + "nothing quantum about this; there weren\\'t even any complex numbers\n", + "involved. A *unitary design* extends this concept from\n", + "evenly-distributed points to evenly-distributed unitaries. More\n", + "formally, instead of averaging polynomials over spheres, we consider\n", + "polynomials that are functions of the entries of unitary matrices,.\n", + "\n", + "::: {.admonition .defn}\n", + "Definition\n", + "\n", + "Let $P_{t,t}(U)$ be a polynomial with homogeneous degree at most $t$ in\n", + "$d$ variables in the entries of a unitary matrix $U$, and degree $t$ in\n", + "the complex conjugates of those entries. A unitary $t$-design is a set\n", + "of $K$ unitaries $\\{U_k\\}$ such that\n", + "\n", + "$$\\frac{1}{K} \\sum_{k=1}^{K} P_{t,t}(U_k) = \\int_{\\mathcal{U}(d)}\n", + "P_{t,t} (U) d\\mu(U)$$\n", + "\n", + "holds for all possible $P_{t,t}$, and where $d\\mu$ is the uniform *Haar\n", + "measure*.\n", + ":::\n", + "\n", + "We stress again that this expression is **exact**. The unitaries in a\n", + "unitary design are a representative set of points that are \\\"evenly\n", + "spaced\\\" across the unitary group. With just a subset of the full group,\n", + "we can evaluate complex expressions that would be otherwise intractable.\n", + "\n", + "A surprising result about unitary designs is that they exist for all\n", + "possible combinations of $t$ and $d$. There are some known lower bounds\n", + "for the number of unitaries required; for example, a 2-design in\n", + "dimension $d$ has at least $d^4 - 2d^2 + 2$ elements, . However,\n", + "actually finding the sets (and in particular, finding ones with minimal\n", + "size), is a challenging problem, though very recently some constructions\n", + "have been put forward.\n", + "\n", + "::: {.admonition}\n", + "Fun fact\n", + "\n", + "Applying the elements of a unitary design to a fixed pure state produces\n", + "a set of vectors that form a *complex projective design*. These are much\n", + "like spherical designs, but they live in a complex vector space. If\n", + "you\\'ve ever studied the characterization of quantum systems, you may\n", + "have come across some special sets of measurements called mutually\n", + "unbiased bases (MUBs), or symmetric, informationally complete positive\n", + "operator valued measurements (SIC-POVMs). Both of these sets of vectors\n", + "are complex projective 2-designs.\n", + "\n", + "{.align-center\n", + "width=\"80.0%\"}\n", + ":::\n", + "\n", + "Unitary $t$-designs in action\n", + "-----------------------------\n", + "\n", + "Unitary designs come into play in applications that require\n", + "randomization, or sampling of random unitaries---essentially, they can\n", + "be used as a stand-in for the Haar measure. The way in which the\n", + "unitaries are used in the application may place restrictions on the\n", + "value of $t$ that is required; arguably the most common is the unitary\n", + "2-design.\n", + "\n", + "While in general unitary designs are hard to construct, there are well\n", + "known results for unitary 1-, 2-, and 3-designs based on familiar\n", + "objects in quantum computing. Before we see what those are, let\\'s\n", + "explore an important use case.\n", + "\n", + "Average fidelity\n", + "================\n", + "\n", + "A key application of unitary 2-designs is benchmarking quantum\n", + "operations. Suppose we have a noisy quantum channel $\\Lambda$ that\n", + "should perform something close to the unitary operation $V$. What can we\n", + "say about the performance of this channel?\n", + "\n", + "One metric of interest is the *fidelity*. Consider the state\n", + "$|0\\rangle$. In an ideal case, we apply $V$ and obtain $V|0\\rangle$. But\n", + "applying the channel $\\Lambda$ gives us something a little different.\n", + "Since it\\'s noisy, we must consider the state as a density matrix. The\n", + "action of $\\Lambda$ on our starting state is\n", + "$\\Lambda(|0\\rangle \\langle 0|)$. If $\\Lambda$ was perfect, then\n", + "$\\Lambda(|0\\rangle \\langle 0|) = V|0\\rangle \\langle\n", + "0|V^\\dagger$, and the fidelity is\n", + "\n", + "$$F(\\Lambda, V) = \\langle 0 | V^\\dagger \\cdot \\Lambda(|0\\rangle \\langle 0|) \\cdot V|0\\rangle = 1.$$\n", + "\n", + "In reality, $\\Lambda$ is not going to implement $V$ perfectly, and\n", + "$F < 1$. More importantly though, all we\\'ve computed so far is the\n", + "fidelity when the initial state is $|0\\rangle$. What if the initial\n", + "state is something different? What is the fidelity *on average*?\n", + "\n", + "To compute an average fidelity, we must do so with respect to the full\n", + "set of Haar-random states. We usually generate random states by applying\n", + "a Haar-random unitary $U$ to $|0\\rangle$. Thus to compute the average\n", + "over all such $U$ we must evaluate\n", + "\n", + "$$\\bar{F}(\\Lambda, V) = \\int_{\\mathcal{U}} d\\mu(U) \\langle 0 | U^\\dagger V^\\dagger \\Lambda(U |0\\rangle \\langle 0| U^\\dagger) V U |0\\rangle.$$\n", + "\n", + "This is known as *twirling* the channel $\\Lambda$. Computing the average\n", + "fidelity in this way would be a nightmare---we\\'d have to compute the\n", + "fidelity with respect to an infinite number of states!\n", + "\n", + "However, consider the expression in the integral above. We have an inner\n", + "product involving two instances of $U$, and two instances of\n", + "$U^\\dagger$. This means that the expression is a polynomial of degree 2\n", + "in both the elements of $U$ and its complex conjugates---this matches\n", + "exactly the definition of a unitary 2-design. This means that if we can\n", + "find a set of $K$ unitaries that form a 2-design, we can compute the\n", + "average fidelity using only a finite set of initial states:\n", + "\n", + "$$\\frac{1}{K} \\sum_{j=1}^K \\langle 0 | U_j^\\dagger V^\\dagger \\Lambda(U_j |0\\rangle \\langle 0|\n", + "U_j^\\dagger) V^\\dagger U_j |0\\rangle = \\int_{\\mathcal{U}} d\\mu(U) \\langle 0\n", + "| U^\\dagger V^\\dagger \\Lambda(U |0\\rangle \\langle 0| U^\\dagger) V U |0\\rangle$$\n", + "\n", + "This is great, but a question remains: what is the representative set of\n", + "unitaries?\n", + "\n", + "The Clifford group\n", + "==================\n", + "\n", + "A beautiful result in quantum computing is that some special groups you\n", + "may already be familiar with are unitary designs:\n", + "\n", + "- the Pauli group forms a unitary 1-design, and\n", + "- the Clifford group forms a unitary 3-design.\n", + "\n", + "By the definition of designs, this means the Clifford group is also a\n", + "1-and 2-design.\n", + "\n", + "The $n$-qubit Pauli group, $\\mathcal{P}(n)$, is the set of all tensor\n", + "products of Pauli operations $X$, $Y$, $Z$, and $I$. The $n$-qubit\n", + "Clifford group, $\\mathcal{C}(n)$, is the *normalizer* of the Pauli\n", + "group. In simpler terms, the Clifford group is the set of operations\n", + "that send Paulis to Paulis (up to a phase) under conjugation i.e.,\n", + "\n", + "$$C P C^\\dagger = \\pm P^\\prime, \\quad \\forall P, P^\\prime \\in \\mathcal{P}(n), \\quad C \\in \\mathcal{C}(n).$$\n", + "\n", + "The Clifford group has some profoundly interesting properties and\n", + "countless uses across quantum computing, from circuit compilation to\n", + "error correcting codes. For a single qubit, the group is built from just\n", + "two operations. One is the Hadamard:\n", + "\n", + "$$H X H^\\dagger = Z, \\quad H Y H^\\dagger = -Y, \\quad H Z H^\\dagger = X.$$\n", + "\n", + "This clearly maps Paulis to Paulis (up to a phase). The other is the\n", + "phase gate $S$:\n", + "\n", + "$$S X S^\\dagger = Y, \\quad S Y S^\\dagger = -X, \\quad S Z S^\\dagger = Z.$$\n", + "\n", + "If both $H$ and $S$ map Paulis to Paulis, then products of them do as\n", + "well. In group theory terms, the single-qubit Clifford group is\n", + "generated by $H$ and $S$. For example, consider the action of $HS$:\n", + "\n", + "$$(HS) X (HS)^\\dagger = -Y, \\quad (HS) Y (HS)^\\dagger = -Z, \\quad (HS) Z (HS)^\\dagger = X.$$\n", + "\n", + "Since $Y = iXZ$, it is enough to specify Clifford operations by how they\n", + "act on $X$ and $Z$. For a particular Clifford, there are 6 possible ways\n", + "it can transform $X$, namely $\\pm X, \\pm Y$, or $\\pm Z$. Once that is\n", + "determined, there are four remaining options for the transformation of\n", + "$Z$, leading to 24 elements total.\n", + "\n", + "It takes some work, but you can take combinations of $H$ and $S$ and\n", + "evaluate their action on $X$ and $Z$ (or look at their matrix\n", + "representations) until you find all 24 unique elements. The results of\n", + "this endeavour are expressed below as strings:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "htyWtrFVwQly" + }, + "outputs": [], + "source": [ + "single_qubit_cliffords = [\n", + " '',\n", + " 'H', 'S',\n", + " 'HS', 'SH', 'SS',\n", + " 'HSH', 'HSS', 'SHS', 'SSH', 'SSS',\n", + " 'HSHS', 'HSSH', 'HSSS', 'SHSS', 'SSHS',\n", + " 'HSHSS', 'HSSHS', 'SHSSH', 'SHSSS', 'SSHSS',\n", + " 'HSHSSH', 'HSHSSS', 'HSSHSS'\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pGo_-fX8wQlz" + }, + "source": [ + "To see for yourself how this set of unitaries is evenly distributed, try\n", + "applying each of the Cliffords to the initial state $|0\\rangle$, and\n", + "plot the resulting states on the Bloch sphere. You\\'ll find they are\n", + "symmetric and evenly spaced; in fact, they are all eigenstates of $X$,\n", + "$Y$, and $Z$. Furthermore, under the full group action, the result is\n", + "balanced in the sense that each eigenstate is obtained the same number\n", + "of times.\n", + "\n", + "The multi-qubit Clifford group can also be specified by only a small set\n", + "of generators (in fact, only one more than is needed for the\n", + "single-qubit case). Together, $H$, $S$, and CNOT (on every possible\n", + "qubit or pair of qubits) generate the $n$-qubit group. Be careful\n", + "though---the size of the group increases exponentially. The 2-qubit\n", + "group alone has 11520 elements! The size can be worked out in a manner\n", + "analogous to that we used above in the single qubit case: by looking at\n", + "the combinatorics of the possible ways the gates can map Paulis with\n", + "only $X$ and $Z$ to other Paulis.\n", + "\n", + "An experiment\n", + "=============\n", + "\n", + "The whole idea of unitary designs may sound too good to be true. Can we\n", + "*really* compute the exact average fidelity using just 24 operations? In\n", + "this section, we put them to the test: we\\'ll compute the average\n", + "fidelity of an operation first with experiments using a large but finite\n", + "amount of Haar-random unitaries, and then again with only the Clifford\n", + "group.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "0D3Bw8IXwQlz" + }, + "outputs": [], + "source": [ + "import pennylane as qml\n", + "\n", + "# Scipy allows us to sample Haar-random unitaries directly\n", + "from scipy.stats import unitary_group\n", + "\n", + "# set the random seed\n", + "np.random.seed(42)\n", + "\n", + "# Use the mixed state simulator\n", + "dev = qml.device(\"default.mixed\", wires=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AooBqsfDwQl0" + }, + "source": [ + "Let\\'s set up a noisy quantum channel. To keep things simple, assume it\n", + "consists of applying `~.pennylane.SX`{.interpreted-text role=\"class\"},\n", + "the square-root of $X$ gate, followed by a few different types of noise.\n", + "First, write a quantum function for our ideal experiment:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "yZtqAXHkwQl0" + }, + "outputs": [], + "source": [ + "def ideal_experiment():\n", + " qml.SX(wires=0)\n", + " return qml.state()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XZX2g2SswQl1" + }, + "source": [ + "Next, we apply some noise. We do so by making use of a relatively new\n", + "feature in PennyLane called [quantum function\n", + "transforms](https://pennylane.readthedocs.io/en/latest/code/qml_transforms.html).\n", + "Such transforms work by modifying the underlying, low-level quantum\n", + "tapes which queue the quantum operations. Suppose the noisy channel is\n", + "composed of the following:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "iWgu5nLXwQl1" + }, + "outputs": [], + "source": [ + "def noisy_operations(damp_factor, depo_factor, flip_prob):\n", + " qml.AmplitudeDamping(damp_factor, wires=0)\n", + " qml.DepolarizingChannel(depo_factor, wires=0)\n", + " qml.BitFlip(flip_prob, wires=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hXMC2ARewQl1" + }, + "source": [ + "Let\\'s create a transform that applies this noise to any quantum\n", + "function *after* the original operations, but before the measurements.\n", + "We use the convenient\n", + "`~.pennylane.transforms.qfunc_transform`{.interpreted-text role=\"func\"}\n", + "decorator:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "lhN6Ge4VwQl1" + }, + "outputs": [], + "source": [ + "@qml.qfunc_transform\n", + "def apply_noise(tape, damp_factor, depo_factor, flip_prob):\n", + " # Apply the original operations\n", + " for op in tape.operations:\n", + " qml.apply(op)\n", + "\n", + " # Apply the noisy sequence\n", + " noisy_operations(damp_factor, depo_factor, flip_prob)\n", + "\n", + " # Apply the original measurements\n", + " for m in tape.measurements:\n", + " qml.apply(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nV2Mpa88wQl2" + }, + "source": [ + "We can now apply this transform to create a noisy version of our ideal\n", + "quantum function:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "N1eG4TFWwQl2" + }, + "outputs": [], + "source": [ + "# The strengths of various types of noise\n", + "damp_factor = 0.02\n", + "depo_factor = 0.02\n", + "flip_prob = 0.001\n", + "\n", + "noisy_experiment = apply_noise(damp_factor, depo_factor, flip_prob)(ideal_experiment)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EomIg-qmwQl2" + }, + "source": [ + "The last part of the experiment involves applying a random unitary\n", + "matrix before all the operations, and its inverse right before the\n", + "measurements. We can write another transform here to streamline this\n", + "process:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "kNLUeoQAwQl2" + }, + "outputs": [], + "source": [ + "@qml.qfunc_transform\n", + "def conjugate_with_unitary(tape, matrix):\n", + " qml.QubitUnitary(matrix, wires=0)\n", + "\n", + " for op in tape.operations:\n", + " qml.apply(op)\n", + "\n", + " qml.QubitUnitary(matrix.conj().T, wires=0)\n", + "\n", + " for m in tape.measurements:\n", + " qml.apply(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BbICvvouwQl3" + }, + "source": [ + "Finally, in order to perform a comparison, we need a function to compute\n", + "the [fidelity](https://en.wikipedia.org/wiki/Fidelity_of_quantum_states)\n", + "compared to the ideal operation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "aDrUBq_awQl3" + }, + "outputs": [], + "source": [ + "from scipy.linalg import sqrtm\n", + "\n", + "def fidelity(rho, sigma):\n", + " # Inputs rho and sigma are density matrices\n", + " sqrt_sigma = sqrtm(sigma)\n", + " fid = np.trace(sqrtm(sqrt_sigma @ rho @ sqrt_sigma))\n", + " return fid.real" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "77i6U6C4wQl3" + }, + "source": [ + "Let\\'s now compute the average fidelity, averaging over 50000\n", + "Haar-random unitaries:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "-SWuQCvHwQl3", + "outputId": "f38c4b73-9781-45c4-f407-77c8fb5e429f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mean fidelity = 0.9897880691822295\n" + ] + } + ], + "source": [ + "n_samples = 50000\n", + "\n", + "fidelities = []\n", + "\n", + "for _ in range(n_samples):\n", + " # Select a Haar-random unitary\n", + " U = unitary_group.rvs(2)\n", + "\n", + " # Apply transform to construct the ideal and noisy quantum functions\n", + " conjugated_ideal_experiment = conjugate_with_unitary(U)(ideal_experiment)\n", + " conjugated_noisy_experiment = conjugate_with_unitary(U)(noisy_experiment)\n", + "\n", + " # Use the functions to create QNodes\n", + " ideal_qnode = qml.QNode(conjugated_ideal_experiment, dev)\n", + " noisy_qnode = qml.QNode(conjugated_noisy_experiment, dev)\n", + "\n", + " # Execute the QNodes\n", + " ideal_state = ideal_qnode()\n", + " noisy_state = noisy_qnode()\n", + "\n", + " # Compute the fidelity\n", + " fidelities.append(fidelity(ideal_state, noisy_state))\n", + "\n", + "fid_mean = np.mean(fidelities)\n", + "print(f\"Mean fidelity = {fid_mean}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hneAjRLbwQl3" + }, + "source": [ + "Now let\\'s repeat the procedure using only Clifford group elements.\n", + "First, we write a quantum function that performs a Clifford operation\n", + "(or its inverse) based on its string representation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "id": "YAaqy1W0wQl4" + }, + "outputs": [], + "source": [ + "def apply_single_clifford(clifford_string, inverse=False):\n", + " for gate in clifford_string:\n", + " if gate == 'H':\n", + " qml.Hadamard(wires=0)\n", + " else:\n", + " sign = -1 if inverse else 1\n", + " qml.PhaseShift(sign * np.pi/2, wires=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2adwduIowQl4" + }, + "source": [ + "Next, we write a transform that applies a Clifford in the context of the\n", + "full experiment, i.e., apply the Clifford, then the operations, followed\n", + "by the inverse of the Clifford.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "id": "pI-3y8HzwQl4" + }, + "outputs": [], + "source": [ + "@qml.qfunc_transform\n", + "def conjugate_with_clifford(tape, clifford_string):\n", + " apply_single_clifford(clifford_string, inverse=False)\n", + "\n", + " for op in tape.operations:\n", + " qml.apply(op)\n", + "\n", + " apply_single_clifford(clifford_string, inverse=True)\n", + "\n", + " for m in tape.measurements:\n", + " qml.apply(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0kaDBhE5wQl4" + }, + "source": [ + "You may have noticed this transform has exactly the same form as\n", + "`conjugate_with_unitary` from above. Only the input type has changed,\n", + "since the application of Cliffords here is specified by their string\n", + "representation.\n", + "\n", + "It\\'s now time to run the experiments:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "id": "85LSAbLpwQl4" + }, + "outputs": [], + "source": [ + "fidelities = []\n", + "\n", + "for C in single_qubit_cliffords:\n", + " conjugated_ideal_experiment = conjugate_with_clifford(C)(ideal_experiment)\n", + " conjugated_noisy_experiment = conjugate_with_clifford(C)(noisy_experiment)\n", + "\n", + " ideal_qnode = qml.QNode(conjugated_ideal_experiment, dev)\n", + " noisy_qnode = qml.QNode(conjugated_noisy_experiment, dev)\n", + "\n", + " ideal_state = ideal_qnode()\n", + " noisy_state = noisy_qnode()\n", + "\n", + " fidelities.append(fidelity(ideal_state, noisy_state))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cx0-SuYBwQl4" + }, + "source": [ + "Let\\'s see how our results compare to the earlier simulation:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "P846UtObwQl5", + "outputId": "0b56a42e-5966-432f-a90f-d4bec4ee4cfa" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Haar-random mean fidelity = 0.9897880691822295\n", + "Clifford mean fidelity = 0.9897181975790679\n" + ] + } + ], + "source": [ + "clifford_fid_mean = np.mean(fidelities)\n", + "\n", + "print(f\"Haar-random mean fidelity = {fid_mean}\")\n", + "print(f\"Clifford mean fidelity = {clifford_fid_mean}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kGKdKRmnwQl5" + }, + "source": [ + "Incredible 🤯 🤯 🤯 We were able to compute the average fidelity using only\n", + "24 experiments. Furthermore, the mean fidelity obtained from the\n", + "Clifford experiments is **exact**; even with 50000 Haar-random\n", + "experiments, we see deviations starting a few decimal places in.\n", + "Consider the resources that would be saved if you were actually\n", + "implementing this in a lab! It\\'s not hard to see why the Clifford group\n", + "plays such an important role in characterization procedures.\n", + "\n", + "Conclusion\n", + "==========\n", + "\n", + "In this demo, we\\'ve barely scratched the surface of designs and their\n", + "applications in quantum computing. While benchmarking is a key\n", + "application area, there are many others. The Pauli group as a unitary\n", + "1-design has applications in the construction of private quantum\n", + "channels[^1]. *Approximate* unitary $t$-designs (where the equality in\n", + "the definition is replaced by approximately equal up to some finite\n", + "precision) are also of interest, as there ways to construct them that\n", + "are more efficient than those of exact designs[^2]. In particular, it\n", + "has been shown that approximate complex projective 4-designs have\n", + "applications to the state discrimination problem[^3].\n", + "\n", + "Furthermore, unitary designs are not the only designs that you\\'ll\n", + "encounter in quantum computing. The familiar Hadamard gate is just a\n", + "2-dimensional example of a broader family of *Hadamard designs*, on\n", + "which there has been extensive research[^4]. Some sets of [mutually\n", + "orthogonal Latin\n", + "squares](https://en.wikipedia.org/wiki/Mutually_orthogonal_Latin_squares)\n", + "have a direct correspondence with mutually unbiased bases, which are\n", + "optimal quantum measurements[^5], as well as complex projective designs;\n", + "and Latin squares themselves have direct correspondence with affine and\n", + "projective planes, bringing us full circle back to the Fano plane from\n", + "which we began.\n", + "\n", + "{.align-center\n", + "width=\"80.0%\"}\n", + "\n", + "References\n", + "==========\n", + "\n", + "About the author\n", + "================\n", + "\n", + "[^1]: A. Ambainis, M. Mosca, A. Tapp, and R. de Wolf (2000) *Private\n", + " Quantum Channels*. Proc. 41st FOCS, 547-553.\n", + " [(PDF)](https://homepages.cwi.nl/~rdewolf/publ/qc/AMTW00.pdf).\n", + "\n", + "[^2]: C. Dankert, R. Cleve, J. Emerson, and E. Levine (2009) *Exact and\n", + " Approximate Unitary 2-Designs: Constructions and Applications.*\n", + " Phys. Rev. A 80, 012304.\n", + " [(arXiv)](https://arxiv.org/abs/quant-ph/0606161).\n", + "\n", + "[^3]: A. Ambainis and J. Emerson (2007) *Quantum t-designs: t-wise\n", + " independence in the quantum world.* Twenty-Second Annual IEEE\n", + " Conference on Computational Complexity 129-140.\n", + " [(arXiv)](https://arxiv.org/abs/quant-ph/0701126).\n", + "\n", + "[^4]: J. Seberry and M. Yamada (1992) *Hadamard matrices, sequences, and\n", + " block designs.* Contemporary Design Theory -- A Collection of\n", + " Surveys (D. J. Stinson and J. Dinitz, Eds.), John Wiley and Sons,\n", + " 431-560. [(PDF)](http://mathscinet.ru/files/YamadaSeberry1992.pdf).\n", + "\n", + "[^5]: M. Gaeta, O. Di Matteo, A. B. Klimov, and H. de Guise (2014)\n", + " *Discrete phase-space approach to mutually orthogonal Latin\n", + " squares*. J. Phys. A: Math. Theor. 47 (43) 435303.\n", + " [(arXiv)](https://arxiv.org/abs/1408.6742).\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" + }, + "colab": { + "provenance": [] + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}