Switch to side-by-side view

--- a
+++ b/Code/All Qiskit, PennyLane QML Nov 23/24a2 Kernels 50% No Train kkawchak.ipynb
@@ -0,0 +1,1175 @@
+{
+  "cells": [
+    {
+      "cell_type": "code",
+      "execution_count": 122,
+      "metadata": {
+        "id": "IZi5TUAp-1Lt",
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 0
+        },
+        "outputId": "1b8c7132-47e7-445b-a4bd-56ca35c5ecb6"
+      },
+      "outputs": [
+        {
+          "output_type": "stream",
+          "name": "stdout",
+          "text": [
+            "Time in seconds since beginning of run: 1700544102.175086\n",
+            "Tue Nov 21 05:21:42 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",
+        "![](../demonstrations/kernels_module/linear_classification.png){.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",
+        "![](../demonstrations/kernels_module/embedding_nonlinear_classification.png){.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": 123,
+      "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": 124,
+      "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": 125,
+      "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": 126,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 406
+        },
+        "id": "9fVgxo-2-1Lw",
+        "outputId": "0f735fd0-c459-4e61-ac47-1f4d79ce56be"
+      },
+      "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": 127,
+      "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": 128,
+      "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": 129,
+      "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": 130,
+      "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": 131,
+      "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": 132,
+      "metadata": {
+        "id": "XND4t8Re-1Lx"
+      },
+      "outputs": [],
+      "source": [
+        "init_params = random_params(num_wires=5, num_layers=6)"
+      ]
+    },
+    {
+      "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": 133,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 0
+        },
+        "id": "EPIxD8tx-1Lx",
+        "outputId": "e29bfa05-23d9-4e0b-ad33-c2bf73dec787"
+      },
+      "outputs": [
+        {
+          "output_type": "stream",
+          "name": "stdout",
+          "text": [
+            "The kernel value between the first and second datapoint is 0.139\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": 134,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 0
+        },
+        "id": "EJ6j5XF1-1Lx",
+        "outputId": "55be5477-faab-41db-eccd-4aa46a4b7690"
+      },
+      "outputs": [
+        {
+          "output_type": "stream",
+          "name": "stdout",
+          "text": [
+            "[[1.    0.139 0.089 0.009 0.012 0.156 0.562 0.216 0.084 0.056 0.03  0.166]\n",
+            " [0.139 1.    0.049 0.012 0.123 0.02  0.226 0.59  0.161 0.023 0.053 0.059]\n",
+            " [0.089 0.049 1.    0.156 0.02  0.158 0.11  0.177 0.608 0.283 0.086 0.104]\n",
+            " [0.009 0.012 0.156 1.    0.139 0.089 0.056 0.03  0.166 0.562 0.216 0.084]\n",
+            " [0.012 0.123 0.02  0.139 1.    0.049 0.023 0.053 0.059 0.226 0.59  0.161]\n",
+            " [0.156 0.02  0.158 0.089 0.049 1.    0.283 0.086 0.104 0.11  0.177 0.608]\n",
+            " [0.562 0.226 0.11  0.056 0.023 0.283 1.    0.618 0.262 0.157 0.184 0.598]\n",
+            " [0.216 0.59  0.177 0.03  0.053 0.086 0.618 1.    0.559 0.184 0.123 0.25 ]\n",
+            " [0.084 0.161 0.608 0.166 0.059 0.104 0.262 0.559 1.    0.598 0.25  0.224]\n",
+            " [0.056 0.023 0.283 0.562 0.226 0.11  0.157 0.184 0.598 1.    0.618 0.262]\n",
+            " [0.03  0.053 0.086 0.216 0.59  0.177 0.184 0.123 0.25  0.618 1.    0.559]\n",
+            " [0.166 0.059 0.104 0.084 0.161 0.608 0.598 0.25  0.224 0.262 0.559 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": 135,
+      "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": 136,
+      "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": 137,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 0
+        },
+        "id": "ZDX6Qvwk-1Ly",
+        "outputId": "3b735d02-6e70-4f0f-d30d-6fa212a896f9"
+      },
+      "outputs": [
+        {
+          "output_type": "stream",
+          "name": "stdout",
+          "text": [
+            "The accuracy of the kernel with random parameters is 0.500\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": 138,
+      "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": 139,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 406
+        },
+        "id": "JQ33viIF-1Ly",
+        "outputId": "1a1f2ff4-871a-4db6-9b51-2723dba46585"
+      },
+      "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": 140,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 0
+        },
+        "id": "N1sKPuO3-1Ly",
+        "outputId": "de815f87-f17f-477d-fee5-5bcf6924ea77"
+      },
+      "outputs": [
+        {
+          "output_type": "stream",
+          "name": "stdout",
+          "text": [
+            "The kernel-target alignment for our dataset and random parameters is 0.041\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": 141,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/",
+          "height": 405
+        },
+        "id": "o8BNL8bP-1Ly",
+        "outputId": "ff07a1be-aa75-4f35-fa7c-3f5c3650ea52"
+      },
+      "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-141-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    246\u001b[0m             \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[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[0;32m--> 248\u001b[0;31m             \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[0m\u001b[1;32m    249\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mcached_device_execution\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[1;32m    250\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    246\u001b[0m             \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[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[0;32m--> 248\u001b[0;31m             \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[0m\u001b[1;32m    249\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mcached_device_execution\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[1;32m    250\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+            "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/transforms/convert_to_numpy_parameters.py\u001b[0m in \u001b[0;36mconvert_to_numpy_parameters\u001b[0;34m(circuit)\u001b[0m\n\u001b[1;32m     84\u001b[0m     \u001b[0mnew_ops\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0m_convert_op_to_numpy_data\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[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moperations\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     85\u001b[0m     \u001b[0mnew_measurements\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0m_convert_measurement_to_numpy_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mm\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmeasurements\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 86\u001b[0;31m     \u001b[0mnew_circuit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnew_ops\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_measurements\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshots\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     87\u001b[0m     \u001b[0;31m# must preserve trainable params as we lose information about the machine learning interface\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     88\u001b[0m     \u001b[0mnew_circuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainable_params\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainable_params\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/tape/qscript.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, ops, measurements, prep, shots, _update)\u001b[0m\n\u001b[1;32m    192\u001b[0m         \u001b[0m_update\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    193\u001b[0m     ):  # pylint: disable=too-many-arguments\n\u001b[0;32m--> 194\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_ops\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mops\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mops\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    195\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mprep\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\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    196\u001b[0m             warnings.warn(\n",
+            "\u001b[0;32m/usr/local/lib/python3.10/dist-packages/pennylane/transforms/convert_to_numpy_parameters.py\u001b[0m in \u001b[0;36m<genexpr>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m     82\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     83\u001b[0m     \"\"\"\n\u001b[0;32m---> 84\u001b[0;31m     \u001b[0mnew_ops\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0m_convert_op_to_numpy_data\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[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moperations\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[0mnew_measurements\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0m_convert_measurement_to_numpy_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mm\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmeasurements\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_circuit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnew_ops\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_measurements\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshots\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/transforms/convert_to_numpy_parameters.py\u001b[0m in \u001b[0;36m_convert_op_to_numpy_data\u001b[0;34m(op)\u001b[0m\n\u001b[1;32m     26\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mop\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     27\u001b[0m     \u001b[0;31m# Use operator method to change parameters when it become available\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 28\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfunctions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbind_new_parameters\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munwrap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\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     29\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     30\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+            "\u001b[0;32m/usr/lib/python3.10/functools.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m    887\u001b[0m                             '1 positional argument')\n\u001b[1;32m    888\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 889\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mdispatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\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[0mkw\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    890\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    891\u001b[0m     \u001b[0mfuncname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'__name__'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'singledispatch function'\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/functions/bind_new_parameters.py\u001b[0m in \u001b[0;36mbind_new_parameters\u001b[0;34m(op, params)\u001b[0m\n\u001b[1;32m     47\u001b[0m     \"\"\"\n\u001b[1;32m     48\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---> 49\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\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[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwires\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdeepcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhyperparameters\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     50\u001b[0m     \u001b[0;32mexcept\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mTypeError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mValueError\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     51\u001b[0m         \u001b[0;31m# operation is doing something different with its call signature.\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   1084\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1085\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1086\u001b[0;31m         \u001b[0;32mif\u001b[0m \u001b[0many\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\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[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\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[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1087\u001b[0m             \u001b[0;31m# if the batch dimension is unknown, then skip the validation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1088\u001b[0m             \u001b[0;31m# this happens when a tensor with a partially known shape is passed, e.g. (None, 12),\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   1084\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1085\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1086\u001b[0;31m         \u001b[0;32mif\u001b[0m \u001b[0many\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\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[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mqml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\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[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1087\u001b[0m             \u001b[0;31m# if the batch dimension is unknown, then skip the validation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1088\u001b[0m             \u001b[0;31m# this happens when a tensor with a partially known shape is passed, e.g. (None, 12),\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     77\u001b[0m         \u001b[0;34m<\u001b[0m\u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTensor\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m91\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshape\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfloat32\u001b[0m\u001b[0;34m>\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     78\u001b[0m     \"\"\"\n\u001b[0;32m---> 79\u001b[0;31m     \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[0m\u001b[1;32m     80\u001b[0m     \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[1;32m     81\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;36mchoose_backend\u001b[0;34m(fn, like, *args, **kwargs)\u001b[0m\n\u001b[1;32m    344\u001b[0m     \"\"\"\n\u001b[1;32m    345\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mlike\u001b[0m \u001b[0;32mis\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[0;32m--> 346\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0m_infer_auto\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[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    347\u001b[0m     \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlike\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\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    348\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mlike\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;36m_default_infer_from_sig\u001b[0;34m(fn, *args, **kwargs)\u001b[0m\n\u001b[1;32m     90\u001b[0m     \u001b[0mevery\u001b[0m \u001b[0mcall\u001b[0m \u001b[0mto\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mdo\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     91\u001b[0m     \"\"\"\n\u001b[0;32m---> 92\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0m_DISPATCHERS\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     93\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     94\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;36mdefault_dispatcher\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m   1283\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1284\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1285\u001b[0;31m \u001b[0;32mdef\u001b[0m \u001b[0mdefault_dispatcher\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[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1286\u001b[0m     \u001b[0;34m\"\"\"Try to infer backend from first argument passed to function.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1287\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0minfer_backend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\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[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
+}