--- a +++ b/Code/All Qiskit, PennyLane QML Nov 23/24a1 Kernels 100% No Train kkawchak.ipynb @@ -0,0 +1,1180 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "id": "IZi5TUAp-1Lt", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "outputId": "78cb9e7d-6215-4978-c1ca-2d0fa4d9c311" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Time in seconds since beginning of run: 1700543496.4327478\n", + "Tue Nov 21 05:11:36 2023\n" + ] + } + ], + "source": [ + "# This cell is added by sphinx-gallery\n", + "# It can be customized to whatever you like\n", + "%matplotlib inline\n", + "# !pip install pennylane\n", + "import time\n", + "seconds = time.time()\n", + "print(\"Time in seconds since beginning of run:\", seconds)\n", + "local_time = time.ctime(seconds)\n", + "print(local_time)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yx4UlxKp-1Lu" + }, + "source": [ + "Training and evaluating quantum kernels\n", + "=======================================\n", + "\n", + "::: {.meta}\n", + ":property=\\\"og:description\\\": Kernels and alignment training with\n", + "Pennylane. :property=\\\"og:image\\\":\n", + "<https://pennylane.ai/qml/_images/QEK_thumbnail.png>\n", + ":::\n", + "\n", + "::: {.related}\n", + "tutorial\\_kernel\\_based\\_training Kernel-based training with\n", + "scikit-learn tutorial\\_data\\_reuploading\\_classifier Data-reuploading\n", + "classifier\n", + ":::\n", + "\n", + "*Authors: Peter-Jan Derks, Paul K. Faehrmann, Elies Gil-Fuster, Tom\n", + "Hubregtsen, Johannes Jakob Meyer and David Wierichs --- Posted: 24 June\n", + "2021. Last updated: 18 November 2021.*\n", + "\n", + "Kernel methods are one of the cornerstones of classical machine\n", + "learning. Here we are concerned with kernels that can be evaluated on\n", + "quantum computers, *quantum kernels* for short. In this tutorial you\n", + "will learn how to evaluate kernels, use them for classification and\n", + "train them with gradient-based optimization, and all that using the\n", + "functionality of PennyLane\\'s [kernels\n", + "module](https://pennylane.readthedocs.io/en/latest/code/qml_kernels.html).\n", + "The demo is based on Ref., a project from Xanadu\\'s own\n", + "[QHack](https://qhack.ai/) hackathon.\n", + "\n", + "What are kernel methods?\n", + "------------------------\n", + "\n", + "To understand what a kernel method does, let\\'s first revisit one of the\n", + "simplest methods to assign binary labels to datapoints: linear\n", + "classification.\n", + "\n", + "Imagine we want to discern two different classes of points that lie in\n", + "different corners of the plane. A linear classifier corresponds to\n", + "drawing a line and assigning different labels to the regions on opposing\n", + "sides of the line:\n", + "\n", + "{.align-center\n", + "width=\"30.0%\"}\n", + "\n", + "We can mathematically formalize this by assigning the label $y$ via\n", + "\n", + "$$y(\\boldsymbol{x}) = \\operatorname{sgn}(\\langle \\boldsymbol{w}, \\boldsymbol{x}\\rangle + b).$$\n", + "\n", + "The vector $\\boldsymbol{w}$ points perpendicular to the line and thus\n", + "determine its slope. The independent term $b$ specifies the position on\n", + "the plane. In this form, linear classification can also be extended to\n", + "higher dimensional vectors $\\boldsymbol{x}$, where a line does not\n", + "divide the entire space into two regions anymore. Instead one needs a\n", + "*hyperplane*. It is immediately clear that this method is not very\n", + "powerful, as datasets that are not separable by a hyperplane can\\'t be\n", + "classified without error.\n", + "\n", + "We can actually sneak around this limitation by performing a neat trick:\n", + "if we define some map $\\phi(\\boldsymbol{x})$ that *embeds* our\n", + "datapoints into a larger *feature space* and then perform linear\n", + "classification there, we could actually realise non-linear\n", + "classification in our original space!\n", + "\n", + "{.align-center\n", + "width=\"65.0%\"}\n", + "\n", + "If we go back to the expression for our prediction and include the\n", + "embedding, we get\n", + "\n", + "$$y(\\boldsymbol{x}) = \\operatorname{sgn}(\\langle \\boldsymbol{w}, \\phi(\\boldsymbol{x})\\rangle + b).$$\n", + "\n", + "We will forgo one tiny step, but it can be shown that for the purpose of\n", + "optimal classification, we can choose the vector defining the decision\n", + "boundary as a linear combination of the embedded datapoints\n", + "$\\boldsymbol{w} = \\sum_i \\alpha_i \\phi(\\boldsymbol{x}_i)$. Putting this\n", + "into the formula yields\n", + "\n", + "$$y(\\boldsymbol{x}) = \\operatorname{sgn}\\left(\\sum_i \\alpha_i \\langle \\phi(\\boldsymbol{x}_i), \\phi(\\boldsymbol{x})\\rangle + b\\right).$$\n", + "\n", + "This rewriting might not seem useful at first, but notice the above\n", + "formula only contains inner products between vectors in the embedding\n", + "space:\n", + "\n", + "$$k(\\boldsymbol{x}_i, \\boldsymbol{x}_j) = \\langle \\phi(\\boldsymbol{x}_i), \\phi(\\boldsymbol{x}_j)\\rangle.$$\n", + "\n", + "We call this function the *kernel*. It provides the advantage that we\n", + "can often find an explicit formula for the kernel $k$ that makes it\n", + "superfluous to actually perform the (potentially expensive) embedding\n", + "$\\phi$. Consider for example the following embedding and the associated\n", + "kernel:\n", + "\n", + "$$\\begin{aligned}\n", + "\\phi((x_1, x_2)) &= (x_1^2, \\sqrt{2} x_1 x_2, x_2^2) \\\\\n", + "k(\\boldsymbol{x}, \\boldsymbol{y}) &= x_1^2 y_1^2 + 2 x_1 x_2 y_1 y_2 + x_2^2 y_2^2 = \\langle \\boldsymbol{x}, \\boldsymbol{y} \\rangle^2.\n", + "\\end{aligned}$$\n", + "\n", + "This means by just replacing the regular scalar product in our linear\n", + "classification with the map $k$, we can actually express much more\n", + "intricate decision boundaries!\n", + "\n", + "This is very important, because in many interesting cases the embedding\n", + "$\\phi$ will be much costlier to compute than the kernel $k$.\n", + "\n", + "In this demo, we will explore one particular kind of kernel that can be\n", + "realized on near-term quantum computers, namely *Quantum Embedding\n", + "Kernels (QEKs)*. These are kernels that arise from embedding data into\n", + "the space of quantum states. We formalize this by considering a\n", + "parameterised quantum circuit $U(\\boldsymbol{x})$ that maps a datapoint\n", + "$\\boldsymbol{x}$ to the state\n", + "\n", + "$$|\\psi(\\boldsymbol{x})\\rangle = U(\\boldsymbol{x}) |0 \\rangle.$$\n", + "\n", + "The kernel value is then given by the *overlap* of the associated\n", + "embedded quantum states\n", + "\n", + "$$k(\\boldsymbol{x}_i, \\boldsymbol{x}_j) = | \\langle\\psi(\\boldsymbol{x}_i)|\\psi(\\boldsymbol{x}_j)\\rangle|^2.$$\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YFfhnwX7-1Lv" + }, + "source": [ + "A toy problem\n", + "=============\n", + "\n", + "In this demo, we will treat a toy problem that showcases the inner\n", + "workings of classification with quantum embedding kernels, training\n", + "variational embedding kernels and the available functionalities to do\n", + "both in PennyLane. We of course need to start with some imports:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "id": "_0dLIZDi-1Lv" + }, + "outputs": [], + "source": [ + "from pennylane import numpy as np\n", + "import matplotlib as mpl\n", + "\n", + "np.random.seed(1359)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rxY0RHIl-1Lv" + }, + "source": [ + "And we proceed right away to create a dataset to work with, the\n", + "`DoubleCake` dataset. Firstly, we define two functions to enable us to\n", + "generate the data. The details of these functions are not essential for\n", + "understanding the demo, so don\\'t mind them if they are confusing.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "id": "-MlPehLl-1Lw" + }, + "outputs": [], + "source": [ + "def _make_circular_data(num_sectors):\n", + " \"\"\"Generate datapoints arranged in an even circle.\"\"\"\n", + " center_indices = np.array(range(0, 2 * num_sectors))\n", + " sector_angle = 2 * np.pi / (2 * num_sectors)\n", + " angles = (center_indices + 0.5) * sector_angle\n", + " x = 0.7 * np.cos(angles)\n", + " y = 0.7 * np.sin(angles)\n", + " labels = 2 * np.remainder(np.floor_divide(angles, sector_angle), 2) - 1\n", + "\n", + " return x, y, labels\n", + "\n", + "\n", + "def make_double_cake_data(num_sectors):\n", + " x1, y1, labels1 = _make_circular_data(num_sectors)\n", + " x2, y2, labels2 = _make_circular_data(num_sectors)\n", + "\n", + " # x and y coordinates of the datapoints\n", + " x = np.hstack([x1, 0.5 * x2])\n", + " y = np.hstack([y1, 0.5 * y2])\n", + "\n", + " # Canonical form of dataset\n", + " X = np.vstack([x, y]).T\n", + "\n", + " labels = np.hstack([labels1, -1 * labels2])\n", + "\n", + " # Canonical form of labels\n", + " Y = labels.astype(int)\n", + "\n", + " return X, Y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sE-g9OvE-1Lw" + }, + "source": [ + "Next, we define a function to help plot the `DoubleCake` data:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "id": "dTzNJQG2-1Lw" + }, + "outputs": [], + "source": [ + "def plot_double_cake_data(X, Y, ax, num_sectors=None):\n", + " \"\"\"Plot double cake data and corresponding sectors.\"\"\"\n", + " x, y = X.T\n", + " cmap = mpl.colors.ListedColormap([\"#FF0000\", \"#0000FF\"])\n", + " ax.scatter(x, y, c=Y, cmap=cmap, s=25, marker=\"s\")\n", + "\n", + " if num_sectors is not None:\n", + " sector_angle = 360 / num_sectors\n", + " for i in range(num_sectors):\n", + " color = [\"#FF0000\", \"#0000FF\"][(i % 2)]\n", + " other_color = [\"#FF0000\", \"#0000FF\"][((i + 1) % 2)]\n", + " ax.add_artist(\n", + " mpl.patches.Wedge(\n", + " (0, 0),\n", + " 1,\n", + " i * sector_angle,\n", + " (i + 1) * sector_angle,\n", + " lw=0,\n", + " color=color,\n", + " alpha=0.1,\n", + " width=0.5,\n", + " )\n", + " )\n", + " ax.add_artist(\n", + " mpl.patches.Wedge(\n", + " (0, 0),\n", + " 0.5,\n", + " i * sector_angle,\n", + " (i + 1) * sector_angle,\n", + " lw=0,\n", + " color=other_color,\n", + " alpha=0.1,\n", + " )\n", + " )\n", + " ax.set_xlim(-1, 1)\n", + "\n", + " ax.set_ylim(-1, 1)\n", + " ax.set_aspect(\"equal\")\n", + " ax.axis(\"off\")\n", + "\n", + " return ax" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5W9DtU_s-1Lw" + }, + "source": [ + "Let\\'s now have a look at our dataset. In our example, we will work with\n", + "3 sectors:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 406 + }, + "id": "9fVgxo-2-1Lw", + "outputId": "7a1cadb3-d727-4eb2-a9e3-257b63fcad2f" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "num_sectors = 3\n", + "X, Y = make_double_cake_data(num_sectors)\n", + "\n", + "ax = plot_double_cake_data(X, Y, plt.gca(), num_sectors=num_sectors)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YiOE-Kte-1Lw" + }, + "source": [ + "Defining a Quantum Embedding Kernel\n", + "===================================\n", + "\n", + "PennyLane\\'s [kernels\n", + "module](https://pennylane.readthedocs.io/en/latest/code/qml_kernels.html)\n", + "allows for a particularly simple implementation of Quantum Embedding\n", + "Kernels. The first ingredient we need for this is an *ansatz*, which we\n", + "will construct by repeating a layer as building block. Let\\'s start by\n", + "defining this layer:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "id": "HoxftykS-1Lw" + }, + "outputs": [], + "source": [ + "import pennylane as qml\n", + "\n", + "\n", + "def layer(x, params, wires, i0=0, inc=1):\n", + " \"\"\"Building block of the embedding ansatz\"\"\"\n", + " i = i0\n", + " for j, wire in enumerate(wires):\n", + " qml.Hadamard(wires=[wire])\n", + " qml.RZ(x[i % len(x)], wires=[wire])\n", + " i += inc\n", + " qml.RY(params[0, j], wires=[wire])\n", + "\n", + " qml.broadcast(unitary=qml.CNOT, pattern=\"ring\", wires=wires) #, parameters=params[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Vz-Xc85B-1Lx" + }, + "source": [ + "To construct the ansatz, this layer is repeated multiple times, reusing\n", + "the datapoint `x` but feeding different variational parameters `params`\n", + "into each of them. Together, the datapoint and the variational\n", + "parameters fully determine the embedding ansatz $U(\\boldsymbol{x})$. In\n", + "order to construct the full kernel circuit, we also require its adjoint\n", + "$U(\\boldsymbol{x})^\\dagger$, which we can obtain via `qml.adjoint`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "id": "0u3Ie4pX-1Lx" + }, + "outputs": [], + "source": [ + "def ansatz(x, params, wires):\n", + " \"\"\"The embedding ansatz\"\"\"\n", + " for j, layer_params in enumerate(params):\n", + " layer(x, layer_params, wires, i0=j * len(wires))\n", + "\n", + "\n", + "adjoint_ansatz = qml.adjoint(ansatz)\n", + "\n", + "\n", + "def random_params(num_wires, num_layers):\n", + " \"\"\"Generate random variational parameters in the shape for the ansatz.\"\"\"\n", + " return np.random.uniform(0, 2 * np.pi, (num_layers, 2, num_wires), requires_grad=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-3A0BtiZ-1Lx" + }, + "source": [ + "Together with the ansatz we only need a device to run the quantum\n", + "circuit on. For the purpose of this tutorial we will use PennyLane\\'s\n", + "`default.qubit` device with 5 wires in analytic mode.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "id": "VajRrjnM-1Lx" + }, + "outputs": [], + "source": [ + "dev = qml.device(\"lightning.qubit\", wires=5, shots=None)\n", + "wires = dev.wires.tolist()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LBQiS_k5-1Lx" + }, + "source": [ + "Let us now define the quantum circuit that realizes the kernel. We will\n", + "compute the overlap of the quantum states by first applying the\n", + "embedding of the first datapoint and then the adjoint of the embedding\n", + "of the second datapoint. We finally extract the probabilities of\n", + "observing each basis state.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "id": "YUoDYzxl-1Lx" + }, + "outputs": [], + "source": [ + "@qml.qnode(dev, interface=\"autograd\")\n", + "def kernel_circuit(x1, x2, params):\n", + " ansatz(x1, params, wires=wires)\n", + " adjoint_ansatz(x2, params, wires=wires)\n", + " return qml.probs(wires=wires)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vp3JMl6k-1Lx" + }, + "source": [ + "The kernel function itself is now obtained by looking at the probability\n", + "of observing the all-zero state at the end of the kernel circuit --\n", + "because of the ordering in `qml.probs`, this is the first entry:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "id": "FzkdR5rH-1Lx" + }, + "outputs": [], + "source": [ + "def kernel(x1, x2, params):\n", + " return kernel_circuit(x1, x2, params)[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v8d7TzEV-1Lx" + }, + "source": [ + "::: {.note}\n", + "::: {.title}\n", + "Note\n", + ":::\n", + "\n", + "An alternative way to set up the kernel circuit in PennyLane would be to\n", + "use the observable type\n", + "[Projector](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.Projector.html).\n", + "This is shown in the [demo on kernel-based training of quantum\n", + "models](https://pennylane.ai/qml/demos/tutorial_kernel_based_training.html),\n", + "where you will also find more background information on the kernel\n", + "circuit structure itself.\n", + ":::\n", + "\n", + "Before focusing on the kernel values we have to provide values for the\n", + "variational parameters. At this point we fix the number of layers in the\n", + "ansatz circuit to $6$.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "id": "XND4t8Re-1Lx" + }, + "outputs": [], + "source": [ + "init_params = random_params(num_wires=5, num_layers=18)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D13009dA-1Lx" + }, + "source": [ + "Now we can have a look at the kernel value between the first and the\n", + "second datapoint:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "EPIxD8tx-1Lx", + "outputId": "c6d1b2da-5640-440d-eb96-90db0ac7135a" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The kernel value between the first and second datapoint is 0.006\n" + ] + } + ], + "source": [ + "kernel_value = kernel(X[0], X[1], init_params)\n", + "print(f\"The kernel value between the first and second datapoint is {kernel_value:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zftXzrUX-1Lx" + }, + "source": [ + "The mutual kernel values between all elements of the dataset form the\n", + "*kernel matrix*. We can inspect it via the\n", + "`qml.kernels.square_kernel_matrix` method, which makes use of symmetry\n", + "of the kernel,\n", + "$k(\\boldsymbol{x}_i,\\boldsymbol{x}_j) = k(\\boldsymbol{x}_j, \\boldsymbol{x}_i)$.\n", + "In addition, the option `assume_normalized_kernel=True` ensures that we\n", + "do not calculate the entries between the same datapoints, as we know\n", + "them to be 1 for our noiseless simulation. Overall this means that we\n", + "compute $\\frac{1}{2}(N^2-N)$ kernel values for $N$ datapoints. To\n", + "include the variational parameters, we construct a `lambda` function\n", + "that fixes them to the values we sampled above.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "EJ6j5XF1-1Lx", + "outputId": "d7e85836-319c-4c16-b44a-5ea11ad2a259" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[1. 0.006 0.048 0.017 0.012 0.018 0.141 0.007 0.007 0.018 0.001 0.012]\n", + " [0.006 1. 0.047 0.012 0.071 0.002 0.013 0.369 0.057 0.001 0.017 0.007]\n", + " [0.048 0.047 1. 0.018 0.002 0.056 0.012 0.022 0.286 0.098 0.015 0.013]\n", + " [0.017 0.012 0.018 1. 0.006 0.048 0.018 0.001 0.012 0.141 0.007 0.007]\n", + " [0.012 0.071 0.002 0.006 1. 0.047 0.001 0.017 0.007 0.013 0.369 0.057]\n", + " [0.018 0.002 0.056 0.048 0.047 1. 0.098 0.015 0.013 0.012 0.022 0.286]\n", + " [0.141 0.013 0.012 0.018 0.001 0.098 1. 0.312 0.018 0.006 0.038 0.303]\n", + " [0.007 0.369 0.022 0.001 0.017 0.015 0.312 1. 0.208 0.038 0.016 0.069]\n", + " [0.007 0.057 0.286 0.012 0.007 0.013 0.018 0.208 1. 0.303 0.069 0.008]\n", + " [0.018 0.001 0.098 0.141 0.013 0.012 0.006 0.038 0.303 1. 0.312 0.018]\n", + " [0.001 0.017 0.015 0.007 0.369 0.022 0.038 0.016 0.069 0.312 1. 0.208]\n", + " [0.012 0.007 0.013 0.007 0.057 0.286 0.303 0.069 0.008 0.018 0.208 1. ]]\n" + ] + } + ], + "source": [ + "init_kernel = lambda x1, x2: kernel(x1, x2, init_params)\n", + "K_init = qml.kernels.square_kernel_matrix(X, init_kernel, assume_normalized_kernel=True)\n", + "\n", + "with np.printoptions(precision=3, suppress=True):\n", + " print(K_init)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nRfsymWl-1Lx" + }, + "source": [ + "Using the Quantum Embedding Kernel for predictions\n", + "==================================================\n", + "\n", + "The quantum kernel alone can not be used to make predictions on a\n", + "dataset, becaues it is essentially just a tool to measure the similarity\n", + "between two datapoints. To perform an actual prediction we will make use\n", + "of scikit-learn\\'s Support Vector Classifier (SVC).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "id": "k6YZKy7J-1Lx" + }, + "outputs": [], + "source": [ + "from sklearn.svm import SVC" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "s3GDnc3R-1Ly" + }, + "source": [ + "To construct the SVM, we need to supply `sklearn.svm.SVC` with a\n", + "function that takes two sets of datapoints and returns the associated\n", + "kernel matrix. We can make use of the function\n", + "`qml.kernels.kernel_matrix` that provides this functionality. It expects\n", + "the kernel to not have additional parameters besides the datapoints,\n", + "which is why we again supply the variational parameters via the `lambda`\n", + "function from above. Once we have this, we can let scikit-learn adjust\n", + "the SVM from our Quantum Embedding Kernel.\n", + "\n", + "::: {.note}\n", + "::: {.title}\n", + "Note\n", + ":::\n", + "\n", + "This step does *not* modify the variational parameters in our circuit\n", + "ansatz. What it does is solving a different optimization task for the\n", + "$\\alpha$ and $b$ vectors we introduced in the beginning.\n", + ":::\n" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "id": "irVOPzix-1Ly" + }, + "outputs": [], + "source": [ + "svm = SVC(kernel=lambda X1, X2: qml.kernels.kernel_matrix(X1, X2, init_kernel)).fit(X, Y)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I4xR2Txu-1Ly" + }, + "source": [ + "To see how well our classifier performs we will measure which percentage\n", + "of the dataset it classifies correctly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "ZDX6Qvwk-1Ly", + "outputId": "53ef9f62-b094-4e10-d18f-5fbf6d46b984" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The accuracy of the kernel with random parameters is 1.000\n" + ] + } + ], + "source": [ + "def accuracy(classifier, X, Y_target):\n", + " return 1 - np.count_nonzero(classifier.predict(X) - Y_target) / len(Y_target)\n", + "\n", + "\n", + "accuracy_init = accuracy(svm, X, Y)\n", + "print(f\"The accuracy of the kernel with random parameters is {accuracy_init:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PPB_3tDn-1Ly" + }, + "source": [ + "We are also interested in seeing what the decision boundaries in this\n", + "classification look like. This could help us spotting overfitting issues\n", + "visually in more complex data sets. To this end we will introduce a\n", + "second helper method.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "id": "EQZNfZjQ-1Ly" + }, + "outputs": [], + "source": [ + "def plot_decision_boundaries(classifier, ax, N_gridpoints=14):\n", + " _xx, _yy = np.meshgrid(np.linspace(-1, 1, N_gridpoints), np.linspace(-1, 1, N_gridpoints))\n", + "\n", + " _zz = np.zeros_like(_xx)\n", + " for idx in np.ndindex(*_xx.shape):\n", + " _zz[idx] = classifier.predict(np.array([_xx[idx], _yy[idx]])[np.newaxis, :])\n", + "\n", + " plot_data = {\"_xx\": _xx, \"_yy\": _yy, \"_zz\": _zz}\n", + " ax.contourf(\n", + " _xx,\n", + " _yy,\n", + " _zz,\n", + " cmap=mpl.colors.ListedColormap([\"#FF0000\", \"#0000FF\"]),\n", + " alpha=0.2,\n", + " levels=[-1, 0, 1],\n", + " )\n", + " plot_double_cake_data(X, Y, ax)\n", + "\n", + " return plot_data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "26F1Bqg9-1Ly" + }, + "source": [ + "With that done, let\\'s have a look at the decision boundaries for our\n", + "initial classifier:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 406 + }, + "id": "JQ33viIF-1Ly", + "outputId": "daf49e6b-3d32-4604-a5b8-d2deab11cda1" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ], + "source": [ + "init_plot_data = plot_decision_boundaries(svm, plt.gca())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TLeU4kMX-1Ly" + }, + "source": [ + "We see the outer points in the dataset can be correctly classified, but\n", + "we still struggle with the inner circle. But remember we have a circuit\n", + "with many free parameters! It is reasonable to believe we can give\n", + "values to those variational parameters which improve the overall\n", + "accuracy of our SVC.\n", + "\n", + "Training the Quantum Embedding Kernel\n", + "=====================================\n", + "\n", + "To be able to train the Quantum Embedding Kernel we need some measure of\n", + "how well it fits the dataset in question. Performing an exhaustive\n", + "search in parameter space is not a good solution because it is very\n", + "resource intensive, and since the accuracy is a discrete quantity we\n", + "would not be able to detect small improvements.\n", + "\n", + "We can, however, resort to a more specialized measure, the\n", + "*kernel-target alignment*. The kernel-target alignment compares the\n", + "similarity predicted by the quantum kernel to the actual labels of the\n", + "training data. It is based on *kernel alignment*, a similiarity measure\n", + "between two kernels with given kernel matrices $K_1$ and $K_2$:\n", + "\n", + "$$\\operatorname{KA}(K_1, K_2) = \\frac{\\operatorname{Tr}(K_1 K_2)}{\\sqrt{\\operatorname{Tr}(K_1^2)\\operatorname{Tr}(K_2^2)}}.$$\n", + "\n", + "::: {.note}\n", + "::: {.title}\n", + "Note\n", + ":::\n", + "\n", + "Seen from a more theoretical side, $\\operatorname{KA}$ is nothing else\n", + "than the cosine of the angle between the kernel matrices $K_1$ and $K_2$\n", + "if we see them as vectors in the space of matrices with the\n", + "Hilbert-Schmidt (or Frobenius) scalar product\n", + "$\\langle A, B \\rangle = \\operatorname{Tr}(A^T B)$. This reinforces the\n", + "geometric picture of how this measure relates to objects, namely two\n", + "kernels, being aligned in a vector space.\n", + ":::\n", + "\n", + "The training data enters the picture by defining an *ideal* kernel\n", + "function that expresses the original labelling in the vector\n", + "$\\boldsymbol{y}$ by assigning to two datapoints the product of the\n", + "corresponding labels:\n", + "\n", + "$$k_{\\boldsymbol{y}}(\\boldsymbol{x}_i, \\boldsymbol{x}_j) = y_i y_j.$$\n", + "\n", + "The assigned kernel is thus $+1$ if both datapoints lie in the same\n", + "class and $-1$ otherwise and its kernel matrix is simply given by the\n", + "outer product $\\boldsymbol{y}\\boldsymbol{y}^T$. The kernel-target\n", + "alignment is then defined as the kernel alignment of the kernel matrix\n", + "$K$ generated by the quantum kernel and\n", + "$\\boldsymbol{y}\\boldsymbol{y}^T$:\n", + "\n", + "$$\\operatorname{KTA}_{\\boldsymbol{y}}(K)\n", + "= \\frac{\\operatorname{Tr}(K \\boldsymbol{y}\\boldsymbol{y}^T)}{\\sqrt{\\operatorname{Tr}(K^2)\\operatorname{Tr}((\\boldsymbol{y}\\boldsymbol{y}^T)^2)}}\n", + "= \\frac{\\boldsymbol{y}^T K \\boldsymbol{y}}{\\sqrt{\\operatorname{Tr}(K^2)} N}$$\n", + "\n", + "where $N$ is the number of elements in $\\boldsymbol{y}$, that is the\n", + "number of datapoints in the dataset.\n", + "\n", + "In summary, the kernel-target alignment effectively captures how well\n", + "the kernel you chose reproduces the actual similarities of the data. It\n", + "does have one drawback, however: having a high kernel-target alignment\n", + "is only a necessary but not a sufficient condition for a good\n", + "performance of the kernel. This means having good alignment is\n", + "guaranteed for good performance, but optimal alignment will not always\n", + "bring optimal training accuracy with it.\n", + "\n", + "Let\\'s now come back to the actual implementation. PennyLane\\'s\n", + "`kernels` module allows you to easily evaluate the kernel target\n", + "alignment:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 0 + }, + "id": "N1sKPuO3-1Ly", + "outputId": "e9623cb5-fdde-42fd-c64b-f8d046443613" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The kernel-target alignment for our dataset and random parameters is 0.145\n" + ] + } + ], + "source": [ + "kta_init = qml.kernels.target_alignment(X, Y, init_kernel, assume_normalized_kernel=True)\n", + "\n", + "print(f\"The kernel-target alignment for our dataset and random parameters is {kta_init:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eOZMWm-p-1Ly" + }, + "source": [ + "Now let\\'s code up an optimization loop and improve the kernel-target\n", + "alignment!\n", + "\n", + "We will make use of regular gradient descent optimization. To speed up\n", + "the optimization we will not use the entire training set to compute\n", + "$\\operatorname{KTA}$ but rather sample smaller subsets of the data at\n", + "each step, we choose $4$ datapoints at random. Remember that\n", + "PennyLane\\'s built-in optimizer works to *minimize* the cost function\n", + "that is given to it, which is why we have to multiply the kernel target\n", + "alignment by $-1$ to actually *maximize* it in the process.\n", + "\n", + "::: {.note}\n", + "::: {.title}\n", + "Note\n", + ":::\n", + "\n", + "Currently, the function `qml.kernels.target_alignment` is not\n", + "differentiable yet, making it unfit for gradient descent optimization.\n", + "We therefore first define a differentiable version of this function.\n", + ":::\n" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 314 + }, + "id": "o8BNL8bP-1Ly", + "outputId": "443ee150-320a-4fd1-f828-fd1c79ba431e" + }, + "outputs": [ + { + "output_type": "error", + "ename": "KeyboardInterrupt", + "evalue": "ignored", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-121-2d12ece3dd74>\u001b[0m in \u001b[0;36m<cell line: 34>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 43\u001b[0m )\n\u001b[1;32m 44\u001b[0m \u001b[0;31m# Optimization step\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 45\u001b[0;31m \u001b[0mparams\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mopt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcost\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 46\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 47\u001b[0m \u001b[0;31m# Report the alignment on the full dataset every 50 steps.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/optimize/gradient_descent.py\u001b[0m in \u001b[0;36mstep\u001b[0;34m(self, objective_fn, grad_fn, *args, **kwargs)\u001b[0m\n\u001b[1;32m 86\u001b[0m \"\"\"\n\u001b[1;32m 87\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 88\u001b[0;31m \u001b[0mg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompute_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobjective_fn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad_fn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mgrad_fn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 89\u001b[0m \u001b[0mnew_args\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 90\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/optimize/gradient_descent.py\u001b[0m in \u001b[0;36mcompute_grad\u001b[0;34m(objective_fn, args, kwargs, grad_fn)\u001b[0m\n\u001b[1;32m 115\u001b[0m \"\"\"\n\u001b[1;32m 116\u001b[0m \u001b[0mg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_gradient\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobjective_fn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgrad_fn\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mgrad_fn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 117\u001b[0;31m \u001b[0mgrad\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 118\u001b[0m \u001b[0mforward\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"forward\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 119\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/_grad.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 116\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 118\u001b[0;31m \u001b[0mgrad_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mans\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgrad_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# pylint: disable=not-callable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 119\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_forward\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mans\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 120\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/autograd/wrap_util.py\u001b[0m in \u001b[0;36mnary_f\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0margnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0munary_operator\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munary_f\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mnary_op_args\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mnary_op_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnary_f\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnary_operator\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/_grad.py\u001b[0m in \u001b[0;36m_grad_with_forward\u001b[0;34m(fun, x)\u001b[0m\n\u001b[1;32m 142\u001b[0m )\n\u001b[1;32m 143\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 144\u001b[0;31m \u001b[0mgrad_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvjp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvspace\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mans\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mones\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 145\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrad_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mans\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/autograd/core.py\u001b[0m in \u001b[0;36mvjp\u001b[0;34m(g)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mvjp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mvspace\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;32mdef\u001b[0m \u001b[0mvjp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mbackward_pass\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend_node\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mvjp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/autograd/core.py\u001b[0m in \u001b[0;36mbackward_pass\u001b[0;34m(g, end_node)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mnode\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtoposort\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mend_node\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0moutgrad\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moutgrads\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0mingrads\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvjp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutgrad\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mparent\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mingrad\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparents\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mingrads\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0moutgrads\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mparent\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0madd_outgrads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutgrads\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparent\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mingrad\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/autograd/core.py\u001b[0m in \u001b[0;36m<lambda>\u001b[0;34m(g)\u001b[0m\n\u001b[1;32m 65\u001b[0m \"VJP of {} wrt argnum 0 not defined\".format(fun.__name__))\n\u001b[1;32m 66\u001b[0m \u001b[0mvjp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvjpfun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mans\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 67\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvjp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 68\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mL\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0margnum_0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margnum_1\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margnums\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/autograd.py\u001b[0m in \u001b[0;36mgrad_fn\u001b[0;34m(dy)\u001b[0m\n\u001b[1;32m 231\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 232\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgradient_fn\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mgradient_fn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"param_shift\"\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mcomputing_jacobian\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 233\u001b[0;31m \u001b[0mjacs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_get_jac_with_caching\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 234\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[0mjacs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mans\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/autograd.py\u001b[0m in \u001b[0;36m_get_jac_with_caching\u001b[0;34m()\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 204\u001b[0m \u001b[0mg_tapes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransforms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap_batch_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpartial_gradient_fn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtapes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 205\u001b[0;31m \u001b[0mres\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mexecute_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg_tapes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mgradient_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 206\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 207\u001b[0m \u001b[0mjacs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mres\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py\u001b[0m in \u001b[0;36minner_execute_with_empty_jac\u001b[0;34m(tapes, **_)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[0;31m# moved to its own explicit step so it will be easier to remove\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 587\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minner_execute_with_empty_jac\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtapes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0m_\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 588\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0minner_execute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtapes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 589\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 590\u001b[0m \u001b[0mexecute_fn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minner_execute_with_empty_jac\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py\u001b[0m in \u001b[0;36minner_execute\u001b[0;34m(tapes, **_)\u001b[0m\n\u001b[1;32m 244\u001b[0m \"\"\"\n\u001b[1;32m 245\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mexpand_fn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 246\u001b[0;31m \u001b[0mtapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexpand_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtapes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 247\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnumpy_only\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0mtapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransforms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconvert_to_numpy_parameters\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtapes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py\u001b[0m in \u001b[0;36m<genexpr>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 244\u001b[0m \"\"\"\n\u001b[1;32m 245\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mexpand_fn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 246\u001b[0;31m \u001b[0mtapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexpand_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtapes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 247\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnumpy_only\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0mtapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransforms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconvert_to_numpy_parameters\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtapes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py\u001b[0m in \u001b[0;36mdevice_expansion_function\u001b[0;34m(tape)\u001b[0m\n\u001b[1;32m 204\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdevice_expansion_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtape\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# pylint: disable=function-redefined\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 205\u001b[0m \u001b[0;34m\"\"\"A wrapper around the device ``expand_fn``.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 206\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpand_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtape\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_expansion\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmax_expansion\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 207\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 208\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdevice_expansion_function\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/_device.py\u001b[0m in \u001b[0;36mexpand_fn\u001b[0;34m(self, circuit, max_expansion)\u001b[0m\n\u001b[1;32m 716\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcustom_expand_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_expansion\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmax_expansion\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 717\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 718\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdefault_expand_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_expansion\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmax_expansion\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 719\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mbatch_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mQuantumTape\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/_device.py\u001b[0m in \u001b[0;36mdefault_expand_fn\u001b[0;34m(self, circuit, max_expansion)\u001b[0m\n\u001b[1;32m 687\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 688\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mops_not_supported\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 689\u001b[0;31m circuit = _local_tape_expand(\n\u001b[0m\u001b[1;32m 690\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdepth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmax_expansion\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstop_at\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstopping_condition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 691\u001b[0m )\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/_device.py\u001b[0m in \u001b[0;36m_local_tape_expand\u001b[0;34m(tape, depth, stop_at)\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhas_decomposition\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mQueuingManager\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstop_recording\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m \u001b[0mobj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mQuantumScript\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecomposition\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_update\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 85\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[0mnew_queue\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/ops/op_math/adjoint.py\u001b[0m in \u001b[0;36mdecomposition\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 273\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecomposition\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhas_adjoint\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 275\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madjoint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 276\u001b[0m \u001b[0mbase_decomp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecomposition\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 277\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mAdjoint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mop\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mreversed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbase_decomp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/ops/qubit/parametric_ops_single_qubit.py\u001b[0m in \u001b[0;36madjoint\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 204\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 205\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0madjoint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 206\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mRY\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwires\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwires\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 207\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 208\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mpow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mz\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/ops/qubit/parametric_ops_single_qubit.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, phi, wires, id)\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 169\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mphi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwires\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 170\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mphi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwires\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwires\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 171\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 172\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mstaticmethod\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/operation.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, wires, id, *params)\u001b[0m\n\u001b[1;32m 1745\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1746\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwires\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1747\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwires\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwires\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1748\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1749\u001b[0m \u001b[0;31m# check the grad_recipe validity\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/operation.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, wires, id, *params)\u001b[0m\n\u001b[1;32m 1050\u001b[0m )\n\u001b[1;32m 1051\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1052\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_batching\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1053\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1054\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/operation.py\u001b[0m in \u001b[0;36m_check_batching\u001b[0;34m(self, params)\u001b[0m\n\u001b[1;32m 1070\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1071\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1072\u001b[0;31m \u001b[0mndims\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndim\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1073\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mValueError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1074\u001b[0m \u001b[0;31m# TODO:[dwierichs] When using tf.function with an input_signature that contains\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/operation.py\u001b[0m in \u001b[0;36m<genexpr>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1070\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1071\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1072\u001b[0;31m \u001b[0mndims\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndim\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1073\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mValueError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1074\u001b[0m \u001b[0;31m# TODO:[dwierichs] When using tf.function with an input_signature that contains\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/autoray/autoray.py\u001b[0m in \u001b[0;36mdo\u001b[0;34m(fn, like, *args, **kwargs)\u001b[0m\n\u001b[1;32m 78\u001b[0m \"\"\"\n\u001b[1;32m 79\u001b[0m \u001b[0mbackend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mchoose_backend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlike\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlike\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 80\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mget_lib_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbackend\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 81\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/math/single_dispatch.py\u001b[0m in \u001b[0;36m<lambda>\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 125\u001b[0m \u001b[0mar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautoray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_MODULE_ALIASES\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"autograd\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"pennylane.numpy\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 126\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 127\u001b[0;31m \u001b[0mar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"autograd\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ndim\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0m_i\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"autograd\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnumpy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndim\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 128\u001b[0m \u001b[0mar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"autograd\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"shape\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0m_i\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"autograd\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnumpy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 129\u001b[0m \u001b[0mar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"autograd\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"flatten\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflatten\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/math/single_dispatch.py\u001b[0m in \u001b[0;36m_i\u001b[0;34m(name)\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mimport_module\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"pennylane\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 36\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mimport_module\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 37\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 38\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.10/importlib/__init__.py\u001b[0m in \u001b[0;36mimport_module\u001b[0;34m(name, package)\u001b[0m\n\u001b[1;32m 124\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 125\u001b[0m \u001b[0mlevel\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 126\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_bootstrap\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_gcd_import\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mlevel\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpackage\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlevel\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 127\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.10/importlib/_bootstrap.py\u001b[0m in \u001b[0;36m_gcd_import\u001b[0;34m(name, package, level)\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.10/importlib/_bootstrap.py\u001b[0m in \u001b[0;36m_find_and_load\u001b[0;34m(name, import_)\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.10/importlib/_bootstrap.py\u001b[0m in \u001b[0;36m__enter__\u001b[0;34m(self)\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.10/importlib/_bootstrap.py\u001b[0m in \u001b[0;36m_get_module_lock\u001b[0;34m(name)\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.10/importlib/_bootstrap.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, name)\u001b[0m\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "def target_alignment(\n", + " X,\n", + " Y,\n", + " kernel,\n", + " assume_normalized_kernel=False,\n", + " rescale_class_labels=True,\n", + "):\n", + " \"\"\"Kernel-target alignment between kernel and labels.\"\"\"\n", + "\n", + " K = qml.kernels.square_kernel_matrix(\n", + " X,\n", + " kernel,\n", + " assume_normalized_kernel=assume_normalized_kernel,\n", + " )\n", + "\n", + " if rescale_class_labels:\n", + " nplus = np.count_nonzero(np.array(Y) == 1)\n", + " nminus = len(Y) - nplus\n", + " _Y = np.array([y / nplus if y == 1 else y / nminus for y in Y])\n", + " else:\n", + " _Y = np.array(Y)\n", + "\n", + " T = np.outer(_Y, _Y)\n", + " inner_product = np.sum(K * T)\n", + " norm = np.sqrt(np.sum(K * K) * np.sum(T * T))\n", + " inner_product = inner_product / norm\n", + "\n", + " return inner_product\n", + "\n", + "\n", + "params = init_params\n", + "opt = qml.GradientDescentOptimizer(0.2)\n", + "\n", + "for i in range(500):\n", + " # Choose subset of datapoints to compute the KTA on.\n", + " subset = np.random.choice(list(range(len(X))), 4)\n", + " # Define the cost function for optimization\n", + " cost = lambda _params: -target_alignment(\n", + " X[subset],\n", + " Y[subset],\n", + " lambda x1, x2: kernel(x1, x2, _params),\n", + " assume_normalized_kernel=True,\n", + " )\n", + " # Optimization step\n", + " params = opt.step(cost, params)\n", + "\n", + " # Report the alignment on the full dataset every 50 steps.\n", + " if (i + 1) % 50 == 0:\n", + " current_alignment = target_alignment(\n", + " X,\n", + " Y,\n", + " lambda x1, x2: kernel(x1, x2, params),\n", + " assume_normalized_kernel=True,\n", + " )\n", + " print(f\"Step {i+1} - Alignment = {current_alignment:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lhbr4YTr-1Lz" + }, + "source": [ + "We want to assess the impact of training the parameters of the quantum\n", + "kernel. Thus, let\\'s build a second support vector classifier with the\n", + "trained kernel:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vUg8wNDJ-1Lz" + }, + "outputs": [], + "source": [ + "# First create a kernel with the trained parameter baked into it.\n", + "trained_kernel = lambda x1, x2: kernel(x1, x2, params)\n", + "\n", + "# Second create a kernel matrix function using the trained kernel.\n", + "trained_kernel_matrix = lambda X1, X2: qml.kernels.kernel_matrix(X1, X2, trained_kernel)\n", + "\n", + "# Note that SVC expects the kernel argument to be a kernel matrix function.\n", + "svm_trained = SVC(kernel=trained_kernel_matrix).fit(X, Y)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LgE9Tcmn-1Lz" + }, + "source": [ + "We expect to see an accuracy improvement vs. the SVM with random\n", + "parameters:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SGUIEF1X-1Lz" + }, + "outputs": [], + "source": [ + "accuracy_trained = accuracy(svm_trained, X, Y)\n", + "print(f\"The accuracy of a kernel with trained parameters is {accuracy_trained:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "N5tAagq1-1Lz" + }, + "source": [ + "We have now achieved perfect classification! 🎆\n", + "\n", + "Following on the results that SVM\\'s have proven good generalisation\n", + "behavior, it will be interesting to inspect the decision boundaries of\n", + "our classifier:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CHXRBR7p-1Lz" + }, + "outputs": [], + "source": [ + "trained_plot_data = plot_decision_boundaries(svm_trained, plt.gca())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "71P66YVn-1Lz" + }, + "source": [ + "Indeed, we see that now not only every data instance falls within the\n", + "correct class, but also that there are no strong artifacts that would\n", + "make us distrust the model. In this sense, our approach benefits from\n", + "both: on one hand it can adjust itself to the dataset, and on the other\n", + "hand is not expected to suffer from bad generalisation.\n", + "\n", + "References\n", + "==========\n", + "\n", + "About the authors\n", + "=================\n" + ] + }, + { + "cell_type": "code", + "source": [ + "seconds = time.time()\n", + "print(\"Time in seconds since end of run:\", seconds)\n", + "local_time = time.ctime(seconds)\n", + "print(local_time)" + ], + "metadata": { + "id": "e8dvgrE6Ktzf" + }, + "execution_count": null, + "outputs": [] + } + ], + "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": [], + "machine_shape": "hm" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}