Skip to content

Instantly share code, notes, and snippets.

@quantshah
Last active July 10, 2024 10:52
Show Gist options
  • Save quantshah/ea1fddb4ec2e61d3a9a5957b791bca92 to your computer and use it in GitHub Desktop.
Save quantshah/ea1fddb4ec2e61d3a9a5957b791bca92 to your computer and use it in GitHub Desktop.
Universal quantum classifier using Pennylane - a python library for quantum machine learning
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Universal quantum classifier\n",
"\n",
"**Author: Shahnawaz Ahmed (shahnawaz.ahmed95@gmail.com)**\n",
"\n",
"A single-qubit quantum circuit which can implement arbitrary unitary operations can be used as a universal classifier much like a single hidden-layered Neural Network. As surprising as it sounds, Adri´an P´erez-Salinas et al. (2019) discuss this with their idea of ´data reuploading´. It is possible to load a single qubit with arbitrary dimensional data and use it as a universal classifier.\n",
"\n",
"In this example, we will implement this idea with `Pennylane` - a python based tool for quantum machine learning, automatic differentiation, and optimization of hybrid quantum-classical computations.\n",
"\n",
"\n",
"### Circles\n",
"\n",
"We consider a simple classification problem will train a single-qubit variational quantum circuit to achieve this goal. The data is generated as a set of random points in a plane $(x_1, x_2)$ and labeled as 1 (blue) or 0 (red) depending on whether they lie inside or outside a circle. \n",
"\n",
"![Screenshot 2019-07-22 14 32 38](https://user-images.githubusercontent.com/6968324/61652759-3bd7ea80-aca8-11e9-88c6-9e9f4d443703.png)\n",
"\n",
"\n",
"### Quantum states, unitaries and data-reuploading\n",
"\n",
"A single-qubit quantum state is characterized by a two-dimensional state vector and can be visualized as a point in the so-called Bloch sphere. Instead of just being a 0 or 1, it can exist as a superposition with say 30% chance of being in the $|0 \\rangle$ and 70% chance of being in the $|1 \\rangle$ state. This is represented by a state vector $|\\psi \\rangle = 0.3|0 \\rangle + 0.7|0 \\rangle $ - the probability \"amplitude\" of the quantum state. In general we can take a vector $(\\alpha, \\beta)$ to represent the probabilities that a qubit can take and visualize it as follows:\n",
"\n",
"![Screenshot 2019-07-22 14 33 49](https://user-images.githubusercontent.com/6968324/61652766-3f6b7180-aca8-11e9-9de5-8efcbd7b63f2.png)\n",
"\n",
"In order to load data onto a single qubit, we use a unitary operation U ($x_1$, $x_2$, $x_3$) which is just a parameterized matrix multiplication representing the rotation of the state vector in the Bloch sphere. Eg., to load $(x_1, x_2)$ into the qubit, we just start from some initial state vector, $|0 \\rangle $, apply the unitary operation $U(x_1, x_2, 0)$ and end up at a new point on the Bloch sphere. Here we have padded 0 since our data is only 2D. Adri´an P´erez-Salinas et al. (2019) discuss how to load a higher dimensional data point ($[x_1, x_2, x_3, x_4, x_5, x_6]$) by breaking it down in sets of three parameters ($U(x_1, x_2, x_3), U(x_4, x_5, x_6)$).\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Model parameters and linear layers with data re-uploading\n",
"\n",
"Once we load the data onto the quantum circuit, we want to have some trainable nonlinear model similar to a Neural Network and a way of learning the weights of the model from data. This is again done with unitaries, $U(\\theta_1, \\theta_2, \\theta_3)$ such that we load the data first and then apply the weights to form a single layer $L(\\vec \\theta, \\vec x) = U(\\vec \\theta)U(\\vec x)$. In principle, this is just application of two matrix multiplications on an input vector initialized to some value. In order to increase the number of trainable parameters (similar to increasing neurons in a single layer of a neural network), we can reapply this layer again and again with new sets of weights, $L(\\vec \\theta_1, \\vec x) L(\\vec \\theta_2, , \\vec x) ... L(\\vec \\theta_L, \\vec x)$ for $L$ layers. The quantum circuit would look like the following:\n",
"\n",
"![Screenshot 2019-07-22 14 49 40](https://user-images.githubusercontent.com/6968324/61652768-41353500-aca8-11e9-819a-8b40380e7dfa.png)\n",
"\n",
"\n",
"\n",
"### Nonlinear \"collapse\" and the cost function\n",
"\n",
"So far, we have only performed linear operations (matrix multiplications) and we know that we need to have some nonlinear squashing similar to activation functions in neural networks to really make a universal classifier (Cybenko 1989). Here is where things gets a bit quantum. After the application of the layers, we will end up at some point on the Bloch sphere due to the sequence of unitaries implementing rotations of the input. These are still just linear transformations of the input state. Now, the output of the model should be a class label which can be encoded as fixed vectors (Blue = $[1, 0]$, Red = $[0, 1]$) on the Bloch sphere. We want to end up at either of them after transforming our input state through alternate applications of data layer and weights. \n",
"\n",
"We can use the idea of the \"collapse\" of our quantum state state into one or other class. This happens when we measure the quantum state which leads to its projection as either the state 0 or 1. We can compute the fidelity (or closeness) of the output state to the class label making the output state jump to either $| 0 \\rangle or |1\\rangle$. By repeating this process several times, we can compute the probability or overlap and assign a label based on which label state, our output has a higher overlap.\n",
"\n",
"<img width=\"1021\" alt=\"Screenshot 2019-07-22 16 47 50\" src=\"https://user-images.githubusercontent.com/6968324/61652769-42666200-aca8-11e9-918e-c7ab5a503a9a.png\">\n",
"\n",
"We can then define the cost function as the sum of the fidelities for all the data points and optimize the parameters $(\\vec \\theta)$ to reduce the cost and get a trained model which can make the classification for new data points.\n",
"\n",
"$$\n",
"\\texttt{Cost} = \\sum_{\\texttt{data points}} (1 - \\texttt{fidelity}(\\psi_{\\texttt{output}}(\\vec x, \\vec \\theta), \\psi_{\\texttt{label}}))\n",
"$$\n",
"\n",
"Now, we can use our favorite optimizer to maximize the sum of the fidelities over all data points (or batches of datapoints) and find the optimal weights for classification.\n",
"\n",
"In `pennylane`, we can define an observable (the expected output label) and make a circuit to return the fidelity using the `Hermitian` operator.\n",
"\n",
"### Multiple qubits, entanglement and Deep Neural Networks\n",
"\n",
"The Universal Approximation Theorem declares a single hidden layered neural network to be capable of approximating any function to arbitrary accuracy. But in practice, it might require a large number of neurons in the single hidden layer and here is where Deep Neural Networks come into action. Deep Neural Networks proved to be better in practice and we have some intuitive idea why, read \"Why does deep and cheap learning work so well?\" by Henry W. Lin, Max Tegmark (MIT) and David Rolnick (2016).\n",
"\n",
"Adri´an P´erez-Salinas et al. (2019) describe that in their approach the \"layers\" $L_i(\\vec \\theta_i, \\vec x )$ are analogous to the size of the intermediate hidden layer of the neural network. And what counts for deep (multiple layers of the neural network) relates to the number of qubits. So, multiple qubits with entanglement between them could provide some quantum advantage over classical neural networks. But here, we will only implement a single qubit classifier.\n",
"\n",
"<img width=\"1088\" alt=\"Screenshot 2019-07-22 17 16 22\" src=\"https://user-images.githubusercontent.com/6968324/61652774-43978f00-aca8-11e9-972e-8d13208055f2.png\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## \"Talk is cheap. Show me the code.\"\n",
"### - Linus Torvalds"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import pennylane as pl\n",
"from pennylane import numpy as np\n",
"from pennylane.optimize import AdamOptimizer, GradientDescentOptimizer\n",
"\n",
"from sklearn.metrics import accuracy_score\n",
"from qutip import Qobj, fidelity, Bloch\n",
"\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Make a dataset of points in and out of a circle"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 360x360 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"def circle(samples, center=[0., 0.], radius=0.8):\n",
" \"\"\"\n",
" Generates a dataset of points with 1/0 labels inside a given radius. \n",
" \n",
" Parameters\n",
" ----------\n",
" samples: int\n",
" The number of samples to generate.\n",
" \n",
" center: tuple\n",
" The center of the circle\n",
" \n",
" radius: float\n",
" The radius of the circle.\n",
" \"\"\"\n",
" Xvals, yvals = [], []\n",
"\n",
" for i in range(samples):\n",
" x = 2*(np.random.rand(2)) - 1\n",
" y = 0\n",
" if np.linalg.norm(x - center) < radius:\n",
" y = 1 \n",
" Xvals.append(x)\n",
" yvals.append(y)\n",
" return np.array(Xvals), np.array(yvals)\n",
"\n",
"\n",
"def plot_data(x, y, fig=None, ax=None):\n",
" \"\"\"\n",
" Plot data with red/blue values for a binary classification.\n",
" \n",
" Parameters\n",
" ----------\n",
" x: ndarray (m, 2)\n",
" An array of m data points with each having dimension 2\n",
" \n",
" y: ndarray (m)\n",
" An array of labels as int (0/1).\n",
" \"\"\"\n",
" if fig == None:\n",
" fig, ax = plt.subplots(1, 1, figsize=(5,5))\n",
" reds = y == 0\n",
" blues = y == 1\n",
" ax.scatter(x[reds, 0], x[reds, 1], c=\"red\",\n",
" s=20, edgecolor='k')\n",
" ax.scatter(x[blues, 0], x[blues, 1], c=\"blue\",\n",
" s=20, edgecolor='k')\n",
" ax.set_xlabel(\"$x_1$\")\n",
" ax.set_ylabel(\"$x_2$\")\n",
"\n",
"\n",
"Xdata, ydata = circle(500)\n",
"plot_data(Xdata, ydata)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Define output labels as quantum state vectors"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def density_matrix(state):\n",
" \"\"\"Calculates the density matrix representation of a state.\n",
"\n",
" Args:\n",
" state (array[complex]): array representing a quantum state vector\n",
" Returns:\n",
" dm: (array[complex]): array representing the density matrix.\n",
" \"\"\"\n",
" return state*np.conj(state).T\n",
"\n",
"label_0 = [[1.0 + 0.j],\n",
" [0. + 0.j]]\n",
"\n",
"label_1 = [[0. + 0.j],\n",
" [1. + 0.j]]\n",
"\n",
"state_labels = [label_0, label_1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Make a simple classifier data reloading circuit"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"dev = pl.device('default.qubit', wires=1)\n",
"# Use your own pennylane-plugin to run on some particular backend\n",
"\n",
"@pl.qnode(dev)\n",
"def qcircuit(var, x=None, y=None):\n",
" \"\"\"A variational quantum circuit representing the Universal classifier.\n",
"\n",
" Args:\n",
" var (array[float]): array of variables\n",
" x (array[float]): single input vector\n",
" y (array[float]): single output state density matrix\n",
"\n",
" Returns:\n",
" float: fidelity between output state and input\n",
" \"\"\"\n",
" for v in var:\n",
" pl.Rot(*x, wires=0)\n",
" pl.Rot(*v, wires=0)\n",
" return pl.expval(pl.Hermitian(y, wires=[0]))\n",
"\n",
"\n",
"def fidelity(state1, state2):\n",
" \"\"\"\n",
" Calculates the fidelity between two state vectors\n",
"\n",
" Args:\n",
" state1 (array[float]): State vector representation\n",
" state2 (array[float]): State vector representation\n",
"\n",
" Returns:\n",
" float: fidelity between `state1` and `state2`\n",
" \"\"\"\n",
" return np.abs(np.dot(np.conj(state1), state2))\n",
"\n",
" \n",
"def cost(weights, x, y, state_labels=None):\n",
" \"\"\"Cost function to be minimized.\n",
"\n",
" Args:\n",
" weights (array[float]): array of weights\n",
" x (array[float]): 2-d array of input vectors.\n",
" y (array[float]): 1-d array of targets.\n",
" state_labels (array[float]): array of state representations for labels\n",
" \n",
" Returns:\n",
" float: loss value to be minimized\n",
" \"\"\"\n",
" # Compute prediction for each input in data batch\n",
" loss = 0.\n",
" dm_labels = [density_matrix(s) for s in state_labels]\n",
" for i in range(len(x)):\n",
" f = qcircuit(weights, x=x[i], y=dm_labels[y[i]])\n",
" loss = loss + (1 - f)\n",
" return loss/len(x)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def test(weights, x, y, state_labels=None):\n",
" \"\"\"\n",
" Tests on a given set of data.\n",
" \n",
" Args:\n",
" weights (array[float]): array of weights\n",
" x (array[float]): 2-d array of input vectors.\n",
" y (array[float]): 1-d array of targets.\n",
" state_labels (array[float]): 1-d array of state representations for labels\n",
" \n",
" Returns:\n",
" predicted (array([int]): predicted labels for test data\n",
" output_states (array[float]): output quantum states from the circuit\n",
" \"\"\"\n",
" fidelity_values = []\n",
" output_states = []\n",
" dm_labels = [density_matrix(s) for s in state_labels]\n",
" for i in range(len(x)):\n",
" expectation = qcircuit(weights, x=x[i], y=dm_labels[y[i]])\n",
" output_states.append(dev._state)\n",
" predicted = predicted_labels(output_states, state_labels)\n",
" return predicted, output_states\n",
"\n",
"\n",
"def predicted_labels(states, state_labels=None):\n",
" \"\"\"\n",
" Computes the label of the predicted state by selecting the one\n",
" with maximum fidelity\n",
" \n",
" Args:\n",
" weights (array[float]): array of weights\n",
" x (array[float]): 2-d array of input vectors.\n",
" y (array[float]): 1-d array of targets.\n",
" state_labels (array[float]): 1-d array of state representations for labels\n",
" \n",
" Returns:\n",
" float: loss value to be minimized\n",
" \"\"\"\n",
" output_labels = [np.argmax([fidelity(s, label) for label in state_labels]) for s in states]\n",
" return np.array(output_labels)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def accuracy_score(y_true, y_pred):\n",
" \"\"\"Accuracy score.\n",
"\n",
" Args:\n",
" y_true (array[float]): 1-d array of targets.\n",
" y_predicted (array[float]): 1-d array of predictions\n",
" state_labels (array[float]): 1-d array of state representations for labels\n",
"\n",
" Returns:\n",
" score : float\n",
" The fraction of correctly classified samples.\n",
" \"\"\"\n",
" score = y_true == y_pred\n",
" return score.sum()/len(y_true)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def iterate_minibatches(inputs, targets, batch_size):\n",
" \"\"\"\n",
" A generator for batches of the input data\n",
" \n",
" Args:\n",
" inputs (array[float]): input data\n",
" targets (array[float]): targets\n",
" \n",
" Returns:\n",
" inputs (array[float]): one batch of input data of length `batch_size`\n",
" targets (array[float]): one batch of targets of length `batch_size`\n",
" \"\"\"\n",
" for start_idx in range(0, inputs.shape[0] - batch_size + 1, batch_size):\n",
" idxs = slice(start_idx, start_idx + batch_size)\n",
" yield inputs[idxs], targets[idxs]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate training and test data"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"num_training = 200\n",
"num_test = 500\n",
"\n",
"Xdata, y_train = circle(num_training)\n",
"X_train = np.hstack((Xdata, np.zeros((Xdata.shape[0], 1))))\n",
"\n",
"Xtest, y_test = circle(num_test)\n",
"X_test = np.hstack((Xtest, np.zeros((Xtest.shape[0], 1))))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Train and evaluate the classifier using gradient descent"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch: 0 | Cost: 0.509948 | Train accuracy 0.515000 | Test Accuracy : 0.508000\n",
"Epoch: 1 | Cost: 0.430845 | Train accuracy 0.605000 | Test Accuracy : 0.654000\n",
"Epoch: 2 | Cost: 0.353277 | Train accuracy 0.730000 | Test Accuracy : 0.748000\n",
"Epoch: 3 | Cost: 0.306970 | Train accuracy 0.765000 | Test Accuracy : 0.790000\n",
"Epoch: 4 | Cost: 0.295256 | Train accuracy 0.820000 | Test Accuracy : 0.808000\n",
"Epoch: 5 | Cost: 0.294993 | Train accuracy 0.805000 | Test Accuracy : 0.792000\n",
"Epoch: 6 | Cost: 0.293592 | Train accuracy 0.805000 | Test Accuracy : 0.790000\n",
"Epoch: 7 | Cost: 0.291759 | Train accuracy 0.815000 | Test Accuracy : 0.804000\n",
"Epoch: 8 | Cost: 0.290740 | Train accuracy 0.830000 | Test Accuracy : 0.808000\n",
"Epoch: 9 | Cost: 0.289939 | Train accuracy 0.820000 | Test Accuracy : 0.810000\n",
"Epoch: 10 | Cost: 0.289068 | Train accuracy 0.830000 | Test Accuracy : 0.808000\n"
]
}
],
"source": [
"num_layers = 3\n",
"learning_rate = 0.1\n",
"epochs = 10\n",
"batch_size = 20\n",
"\n",
"opt = AdamOptimizer(learning_rate, beta1=0.9, beta2=0.999)\n",
"\n",
"# initialize random weights\n",
"weights = np.random.random(size=(num_layers, 3))\n",
"\n",
"predicted_train, states_train = test(weights, X_train, y_train, state_labels)\n",
"accuracy_train = accuracy_score(y_train, predicted_train)\n",
" \n",
"predicted_test, states_test = test(weights, X_test, y_test, state_labels)\n",
"accuracy_test = accuracy_score(y_test, predicted_test)\n",
"\n",
"loss = cost(weights, X_test, y_test, state_labels)\n",
"\n",
"print(\"Epoch: {:2d} | Cost: {:3f} | Train accuracy {:3f} | Test Accuracy : {:3f}\".format(0,\n",
" loss[0],\n",
" accuracy_train,\n",
" accuracy_test))\n",
"for it in range(epochs):\n",
" for Xbatch, ybatch in iterate_minibatches(X_train, y_train, batch_size=batch_size):\n",
" weights = opt.step(lambda v: cost(v, Xbatch, ybatch, state_labels), weights)\n",
" \n",
" predicted_train, states_train = test(weights, X_train, y_train, state_labels)\n",
" accuracy_train = accuracy_score(y_train, predicted_train)\n",
" loss = cost(weights, X_train, y_train, state_labels) \n",
"\n",
" predicted_test, states_test = test(weights, X_test, y_test, state_labels)\n",
" accuracy_test = accuracy_score(y_test, predicted_test)\n",
"\n",
" print(\"Epoch: {:2d} | Cost: {:3f} | Train accuracy {:3f} | Test Accuracy : {:3f}\".format(it + 1,\n",
" loss[0],\n",
" accuracy_train,\n",
" accuracy_test))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Results"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"==========================================================================\n",
"Number of training data points: 200; Number of test data points: 500\n",
"Total number of calls to circuit for training: 2000\n",
"Number of times model parameters were updated: 100\n",
"Cost: 0.289068 | Train accuracy 0.830000 | Test Accuracy : 0.808000\n",
"==========================================================================\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"print(\"==========================================================================\")\n",
"print(\"Number of training data points: {}; Number of test data points: {}\".format(len(X_train), len(X_test)))\n",
"print(\"Total number of calls to circuit for training: {}\".format(int(len(X_train)*epochs)))\n",
"print(\"Number of times model parameters were updated: {:2d}\".format(int(len(X_train)*epochs/batch_size)))\n",
"print(\"Cost: {:3f} | Train accuracy {:3f} | Test Accuracy : {:3f}\".format(loss[0],\n",
" accuracy_train,\n",
" accuracy_test))\n",
"print(\"==========================================================================\")\n",
"\n",
"fig, axes = plt.subplots(1, 2, figsize = (8, 4))\n",
"plot_data(X_test, predicted_test, fig, axes[0])\n",
"plot_data(X_test, y_test, fig, axes[1])\n",
"axes[0].set_title(\"Predictions\")\n",
"axes[1].set_title(\"True data\")\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# References\n",
"\n",
"[1] Pérez-Salinas, Adrián, et al. \"Data re-uploading for a universal quantum classifier.\" arXiv preprint arXiv:1907.02085 (2019)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment