├── README.md
├── Meta-VQE Pennylane.ipynb
└── VQC_Pennylane.ipynb
/README.md:
--------------------------------------------------------------------------------
1 | # pennylane-notebooks
2 | Repository for my pennylane notebooks.
3 |
4 | # Notebooks
5 |
6 | - [Meta-VQE](https://github.com/nahumsa/pennylane-notebooks/blob/main/Meta-VQE%20Pennylane.ipynb) : The Meta-VQE algorithm is a variational quantum algorithm that is suited for NISQ devices and encodes parameters of a Hamiltonian into a variational ansatz which we can obtain good estimations of the ground state of the Hamiltonian by changing only those encoded parameters.
7 | - [Variational Quantum Classifier](https://nbviewer.jupyter.org/github/nahumsa/pennylane-notebooks/blob/main/VQC_Pennylane.ipynb) : The Variational Quantum Classifier is a Hybrid Quantum-Classical algorithm that is used to classify data.
8 |
--------------------------------------------------------------------------------
/Meta-VQE Pennylane.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "Meta-VQE_Pennylane.ipynb",
7 | "provenance": [],
8 | "collapsed_sections": [],
9 | "authorship_tag": "ABX9TyNwv4P4Rs4U0mPBkHn269AQ",
10 | "include_colab_link": true
11 | },
12 | "kernelspec": {
13 | "name": "python3",
14 | "display_name": "Python 3"
15 | }
16 | },
17 | "cells": [
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "id": "view-in-github",
22 | "colab_type": "text"
23 | },
24 | "source": [
25 | "
"
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "metadata": {
31 | "id": "ZzB6C0DClCc7"
32 | },
33 | "source": [
34 | "from IPython.display import clear_output\n",
35 | "! pip install pennylane\n",
36 | "clear_output()"
37 | ],
38 | "execution_count": null,
39 | "outputs": []
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {
44 | "id": "Bt2gyp9eaV0p"
45 | },
46 | "source": [
47 | "# Meta-Variational Quantum Eigensolver"
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "metadata": {
53 | "id": "1R0zp5QTabsW"
54 | },
55 | "source": [
56 | "by Nahum Sá (nahumsa@cbpf.br)"
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "metadata": {
62 | "id": "LdsX1tFOLZi3"
63 | },
64 | "source": [
65 | "# 1) Introduction\n",
66 | "\n",
67 | "The Meta-VQE algorithm is a variational quantum algorithm that is suited for NISQ devices and encodes parameters of a Hamiltonian into a variational ansatz which we can obtain good estimations of the ground state of the Hamiltonian by changing only those encoded parameters. \n",
68 | "\n",
69 | "This leads to a advantage when compared with the original VQE algorithm, because if you want to know the profile of a parametrized Hamiltonian you would need to run the VQE algorithm for each parameter, using Meta-VQE you would only need to run for a fixed set of training parameters and in order to get the profile you would only need to change the parameters of the ansatz.\n",
70 | "\n",
71 | "The Meta-VQE algorithm consists of two parts: \n",
72 | "\n",
73 | "- Encoding;\n",
74 | "- Processing;\n",
75 | "\n",
76 | "Consider a parametrized Hamiltonian $H(\\vec{\\lambda})$, where $\\vec{\\lambda} = \\{ \\lambda_1, \\dots, \\lambda_p \\}$. The circuit is initialized in the $| 0 \\rangle^{\\otimes n}$ and then a encoding layer is added to the circuit, this layer encodes parameters of the Hamiltonian and has training parameters as well. The encoding layer is a unitary $\\mathcal{S} = \\mathcal{S}(\\vec{\\theta}_{\\mathrm{enc}}, \\vec{\\lambda})$. After the encoding layer, we add a processing layer which is a unitary $\\mathcal{U} = \\mathcal{U}(\\vec{\\theta}_{\\mathrm{proc}})$ which consists only of training parameters.\n",
77 | "\n",
78 | "Thus the ansatz can be written as:\n",
79 | "\n",
80 | "$$\n",
81 | "| \\psi_i \\rangle = \\mathcal{U}(\\vec{\\theta}_{\\mathrm{proc}}) \\ \\mathcal{S}(\\vec{\\theta}_{\\mathrm{enc}}, \\vec{\\lambda}) \\ | 0 \\rangle^{\\otimes n }\n",
82 | "$$\n",
83 | "\n",
84 | "After constructing the ansatz we generate a set of training parameters ($\\lambda_i$), which we minimize the cost function:\n",
85 | "\n",
86 | "$$\n",
87 | "\\mathcal{L}_{\\mathrm{COST}} = \\sum_{i=1}^M \\langle \\psi_i | H (\\lambda_i) | \\psi_i \\rangle\n",
88 | "$$\n",
89 | "\n",
90 | "And get optimal parameters $\\vec{\\theta}^*$ and use them to evaluate other parameters of the parametrized Hamiltonian which are not in the training set."
91 | ]
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {
96 | "id": "TRYcTtgvP1KG"
97 | },
98 | "source": [
99 | "For this demo I will be using the XXZ spin chain just like the original [Meta-VQE paper](https://arxiv.org/abs/2009.13545) and is based on the [demo](https://github.com/AlbaCL/qhack21/blob/main/Meta-VQE.ipynb) by Alba Cervera-Lierta written using [Tequila](https://github.com/aspuru-guzik-group/tequila)."
100 | ]
101 | },
102 | {
103 | "cell_type": "markdown",
104 | "metadata": {
105 | "id": "wamSFWksAlW4"
106 | },
107 | "source": [
108 | "## 1.1) Constructing the Hamiltonian"
109 | ]
110 | },
111 | {
112 | "cell_type": "markdown",
113 | "metadata": {
114 | "id": "PaoumNPqP9iv"
115 | },
116 | "source": [
117 | "The XXZ spin chain has the following Hamiltonian:\n",
118 | "\n",
119 | "$$\n",
120 | "\\mathcal{H} = \\sum_{i=1}^N \\big( X_i X_{i+1} + Y_i Y_{i+1} + \\Delta Z_i Z_{i+1} \\big) + \\eta \\sum_{i=1}^N Z_i\n",
121 | "$$\n",
122 | "\n",
123 | "Where $\\Delta$ is the anisotropy parameter and $\\lambda$ the transverse field strenght. This model is interesting because it has two phase transitions when $\\lambda=0$, at $\\Delta = \\pm 1$. Other feature of this model is that for $\\Delta < -1$ the ground state is a product state and if $1 < \\Delta \\leq 1$ the ground state is highly entangled.\n",
124 | "\n",
125 | "I will use periodic boundary conditions, which means that the last spin will have connectivity with the first spin on the chain.\n",
126 | "\n",
127 | "The great question is: **Is it possible to create an ansatz that generate states for any $n$ and $\\Delta$?**\n",
128 | "\n",
129 | "This is where the Meta-VQE comes to rescue!\n",
130 | "\n"
131 | ]
132 | },
133 | {
134 | "cell_type": "code",
135 | "metadata": {
136 | "id": "4nu_L3inI7LO"
137 | },
138 | "source": [
139 | "# Imports\n",
140 | "import numpy as np\n",
141 | "import pennylane as qml\n",
142 | "from tqdm.notebook import tqdm\n",
143 | "import matplotlib.pyplot as plt"
144 | ],
145 | "execution_count": null,
146 | "outputs": []
147 | },
148 | {
149 | "cell_type": "code",
150 | "metadata": {
151 | "id": "TbOeVdmwNmzt"
152 | },
153 | "source": [
154 | "def hamiltonian_XXZ(n_qubits: int, delta: float, eta: float) -> qml.Hamiltonian:\n",
155 | " \"\"\" Creates the XXZ hamiltonian, which is given by:\n",
156 | "\n",
157 | " $$\n",
158 | " \\mathcal{H} = \\sum_{i=1}^N \\big( X_i X_{i+1} + Y_i Y_{i+1} \n",
159 | " + \\Delta Z_i Z_{i+1} \\big) + \\eta \\sum_{i=1}^N Z_i\n",
160 | " $$\n",
161 | "\n",
162 | " Args:\n",
163 | " n_qubits(int): number of spins in the chain.\n",
164 | " delta(float): delta parameter.\n",
165 | " eta(float): eta parameter.\n",
166 | " \"\"\"\n",
167 | " hamiltonian = []\n",
168 | " coeffs = []\n",
169 | " \n",
170 | " # Periodic Boundary Conditions\n",
171 | " for op in [qml.PauliX, qml.PauliY, qml.PauliZ]:\n",
172 | " hamiltonian.append(op(n_qubits-1)@op(0))\n",
173 | " if op != qml.PauliZ :\n",
174 | " coeffs.append(1.)\n",
175 | " else:\n",
176 | " coeffs.append(delta)\n",
177 | " \n",
178 | " hamiltonian.append(qml.PauliZ(n_qubits-1))\n",
179 | " coeffs.append(eta)\n",
180 | "\n",
181 | " for qubits in range(n_qubits - 1):\n",
182 | " for op in [qml.PauliX, qml.PauliY, qml.PauliZ]:\n",
183 | " \n",
184 | " hamiltonian.append(op(qubits)@op(qubits+1))\n",
185 | " \n",
186 | " if op != qml.PauliZ :\n",
187 | " coeffs.append(1.)\n",
188 | " else:\n",
189 | " coeffs.append(delta)\n",
190 | " \n",
191 | " hamiltonian.append(qml.PauliZ(qubits))\n",
192 | " coeffs.append(eta)\n",
193 | "\n",
194 | " H = qml.Hamiltonian(coeffs, hamiltonian, simplify=True)\n",
195 | " return H\n",
196 | "\n",
197 | "def hamiltonian_to_matrix(H: qml.Hamiltonian) -> np.array:\n",
198 | " \"\"\" Converts a pennylane Hamiltonian object into a matrix.\n",
199 | "\n",
200 | " Args:\n",
201 | " H(qml.Hamiltonian): Hamiltonian.\n",
202 | "\n",
203 | " Output:\n",
204 | " np.array: Outputs the matrix representation of the Hamiltonian.\n",
205 | " \"\"\"\n",
206 | " mat = np.zeros((2**n_qubits, 2**n_qubits), np.complex128)\n",
207 | " for coef, op in zip(*H.terms):\n",
208 | " mat += coef*qml.utils.expand(op.matrix, op.wires, n_qubits)\n",
209 | " return mat\n",
210 | "\n",
211 | "def exact_gs(H: qml.Hamiltonian) -> float:\n",
212 | " \"\"\" Calculates the Ground State energy of the Hamiltonian.\n",
213 | "\n",
214 | " Args:\n",
215 | " H(qml.Hamiltonian): Hamiltonian.\n",
216 | "\n",
217 | " Output:\n",
218 | " float: outputs the ground state energy of the Hamiltonian.\n",
219 | " \"\"\"\n",
220 | " matrix = hamiltonian_to_matrix(H)\n",
221 | " energies = np.linalg.eigvals(matrix)\n",
222 | " return np.real(min(energies))"
223 | ],
224 | "execution_count": null,
225 | "outputs": []
226 | },
227 | {
228 | "cell_type": "code",
229 | "metadata": {
230 | "id": "pO_WDYfUOpfW",
231 | "colab": {
232 | "base_uri": "https://localhost:8080/"
233 | },
234 | "outputId": "73e1c93c-1409-4670-b09a-ad0e74fb748b"
235 | },
236 | "source": [
237 | "n_qubits = 2\n",
238 | "delta = 2.\n",
239 | "eta = 2.\n",
240 | "H = hamiltonian_XXZ(n_qubits, delta, eta)\n",
241 | "print(H)"
242 | ],
243 | "execution_count": null,
244 | "outputs": [
245 | {
246 | "output_type": "stream",
247 | "text": [
248 | "(2.0) [X1 X0]\n",
249 | "+ (2.0) [Y1 Y0]\n",
250 | "+ (4.0) [Z1 Z0]\n",
251 | "+ (2.0) [Z1]\n",
252 | "+ (2.0) [Z0]\n"
253 | ],
254 | "name": "stdout"
255 | }
256 | ]
257 | },
258 | {
259 | "cell_type": "markdown",
260 | "metadata": {
261 | "id": "h7rzRH6mQZxm"
262 | },
263 | "source": [
264 | "## 1.2) Creating the ansatz\n",
265 | "\n",
266 | "In order to create an ansatz it is needed to do an encoding of the Hamiltonian parameter. For this case I choose to do a linear encoding of the parameter $\\Delta$ of the XXZ Hamiltonian:\n",
267 | "\n",
268 | "$$\n",
269 | "S(\\Delta, \\theta) = R_Z ( \\theta_0 \\ \\Delta + \\theta_1) R_Y ( \\theta_2 \\ \\Delta + \\theta_3)\n",
270 | "$$\n",
271 | "\n",
272 | "For the processing layer, U, we have that:\n",
273 | "\n",
274 | "$$\n",
275 | "U(\\theta) = R_Z ( \\theta_0 ) R_Y ( \\theta_1)\n",
276 | "$$"
277 | ]
278 | },
279 | {
280 | "cell_type": "code",
281 | "metadata": {
282 | "id": "-carCDUuSh6S"
283 | },
284 | "source": [
285 | "def variational_ansatz(params: np.array, delta: float , wires: qml.wires, H=None):\n",
286 | " \"\"\" Variational ansatz with linear encoding.\n",
287 | "\n",
288 | "\n",
289 | " \"\"\"\n",
290 | " \n",
291 | " n_layers = params.shape[0]\n",
292 | " n_qubits = params.shape[1]\n",
293 | "\n",
294 | " for L in range(n_layers):\n",
295 | " # Encoding Layer\n",
296 | " if L == 0:\n",
297 | " for qubit in range(n_qubits):\n",
298 | " qml.RZ(params[L][qubit][0] * delta + params[L][qubit][1], wires=qubit)\n",
299 | " qml.RY(params[L][qubit][2] * delta + params[L][qubit][3], wires=qubit)\n",
300 | " \n",
301 | " for ent in range(0, n_qubits - 1, 2):\n",
302 | " qml.CNOT(wires= [ent, ent+1])\n",
303 | " \n",
304 | " # Processing Layer\n",
305 | " else:\n",
306 | " for qubit in range(n_qubits):\n",
307 | " qml.RZ(params[L][qubit][0] , wires=qubit)\n",
308 | " qml.RY(params[L][qubit][2] , wires=qubit)\n",
309 | " \n",
310 | " for ent in range(0, n_qubits - 1, 2):\n",
311 | " qml.CNOT(wires= [ent, ent+1])"
312 | ],
313 | "execution_count": null,
314 | "outputs": []
315 | },
316 | {
317 | "cell_type": "markdown",
318 | "metadata": {
319 | "id": "QltU9YSlo3GK"
320 | },
321 | "source": [
322 | "# 1.3) Defining the cost function\n",
323 | "\n",
324 | "The main idea for the cost function is to minimize the energy value over all training points (encoded parameters of the Hamiltonian) $H(\\lambda_i)$, thus I choose the same cost function as the original paper:\n",
325 | "\n",
326 | "$$\n",
327 | "\\mathcal{L}_{\\mathrm{COST}} = \\sum_{i=1}^M \\langle \\psi_i | H (\\lambda_i) | \\psi_i \\rangle\n",
328 | "$$\n",
329 | "\n",
330 | "By minimizing this cost function it is expected to find the ground state by only changing the parameters $\\lambda_i$ on the parametrized wave function $| \\psi_i \\rangle$.\n",
331 | "\n",
332 | "In order to construct the loss function using Pennylane, it is needed to first construct a general way to calculate the expected value of a hamiltonian given an ansatz, which is done on the `ExpvalH`, and then it is neede to calculate the cost of the XXZ Hamiltonian for each training points of the parameter $\\delta$."
333 | ]
334 | },
335 | {
336 | "cell_type": "code",
337 | "metadata": {
338 | "id": "rQDB7ayxgeKm"
339 | },
340 | "source": [
341 | "def ExpvalH(H: qml.Hamiltonian, device: qml.device):\n",
342 | " coeffs, observables = H.terms\n",
343 | " qnodes = qml.map(\n",
344 | " variational_ansatz, observables, device\n",
345 | " )\n",
346 | " cost = qml.dot(coeffs, qnodes)\n",
347 | " return cost\n",
348 | "\n",
349 | "def m_vqe_cost(train_deltas: np.array, dev: qml.device , params: np.array):\n",
350 | " # cost function value\n",
351 | " c = 0.\n",
352 | " n_qubits = dev.num_wires\n",
353 | "\n",
354 | " for delta in train_deltas:\n",
355 | " H = hamiltonian_XXZ(n_qubits, delta, eta)\n",
356 | " cost = ExpvalH(H, dev)\n",
357 | " c += cost(params, delta=delta)\n",
358 | " \n",
359 | " return c"
360 | ],
361 | "execution_count": null,
362 | "outputs": []
363 | },
364 | {
365 | "cell_type": "markdown",
366 | "metadata": {
367 | "id": "eRS5P-3MLwuQ"
368 | },
369 | "source": [
370 | "Let's define the parameters to run the algorithm. First we define the training values of $\\delta$ which is taken to be a uniform distrubution between -1.1 and 1.1. \n",
371 | "\n",
372 | "Next we define the eta to be 0.75 as the original paper, and then define the number of encoded and processing layers.\n",
373 | "\n",
374 | "After that we initialize the parameters at random. "
375 | ]
376 | },
377 | {
378 | "cell_type": "code",
379 | "metadata": {
380 | "id": "O7_YxWxiiu3n"
381 | },
382 | "source": [
383 | "# Creating training data\n",
384 | "n_qubits = 2\n",
385 | "dev = qml.device(\"default.qubit\", wires=n_qubits)\n",
386 | "\n",
387 | "train_deltas = np.random.uniform(low=-1, high=1, size=5)\n",
388 | "\n",
389 | "seed\n",
390 | "# Hyperparameters\n",
391 | "eta = 0.75 # lambda parameter\n",
392 | "L = 4 # Number of layers\n",
393 | "\n",
394 | "# initializing parameters\n",
395 | "params = np.random.uniform(low=-np.pi/2, high=np.pi/2, size=(L, n_qubits, 4))\n",
396 | "\n",
397 | "# Training Parameters\n",
398 | "epochs = 100\n",
399 | "optimizer = qml.AdagradOptimizer()\n",
400 | "\n",
401 | "from functools import partial\n",
402 | "\n",
403 | "# Applyies train_deltas for the Meta-VQE cost function\n",
404 | "cost_fn = partial(m_vqe_cost, train_deltas, dev)\n",
405 | "\n",
406 | "pbar = tqdm(range(epochs), desc='Energy', leave=True)\n",
407 | "\n",
408 | "for i in pbar:\n",
409 | " params, val = optimizer.step_and_cost(cost_fn, params)\n",
410 | " pbar.set_description(f\"Loss: {val:.3f}\")\n",
411 | "\n",
412 | "params_mvqe = params.copy()"
413 | ],
414 | "execution_count": null,
415 | "outputs": []
416 | },
417 | {
418 | "cell_type": "markdown",
419 | "metadata": {
420 | "id": "FYhGN4lOg2gS"
421 | },
422 | "source": [
423 | "## 1.3) Testing the trained model\n",
424 | "\n",
425 | "Now we compare the trained ansatz with the exact solution and see that it \"learns\" the shape of the exact solution, but it has some offset."
426 | ]
427 | },
428 | {
429 | "cell_type": "code",
430 | "metadata": {
431 | "id": "XAqVzd1drXD-"
432 | },
433 | "source": [
434 | "# Creating test data\n",
435 | "test_deltas = np.random.uniform(low=-1, high=1, size=50)\n",
436 | "test_energies = np.zeros_like(test_deltas)\n",
437 | "exact_energies = np.zeros_like(test_deltas)\n",
438 | "\n",
439 | "n_qubits = dev.num_wires\n",
440 | "\n",
441 | "for i, delta in tqdm(enumerate(test_deltas)):\n",
442 | " H = hamiltonian_XXZ(n_qubits, delta, eta)\n",
443 | " cost = ExpvalH(H, dev)\n",
444 | " test_energies[i] = cost(params_mvqe, delta=delta)\n",
445 | " exact_energies[i] = exact_gs(H)"
446 | ],
447 | "execution_count": null,
448 | "outputs": []
449 | },
450 | {
451 | "cell_type": "code",
452 | "metadata": {
453 | "colab": {
454 | "base_uri": "https://localhost:8080/",
455 | "height": 298
456 | },
457 | "id": "_qCe_AuOsSLK",
458 | "outputId": "0e303876-4a35-41f3-f5c8-ad74100a6fd1"
459 | },
460 | "source": [
461 | "plt.plot(test_deltas, test_energies, 'o', label='Meta-VQE')\n",
462 | "plt.plot(test_deltas, exact_energies, 'ro', label='Exact')\n",
463 | "plt.title(\"Test\")\n",
464 | "plt.xlabel(\"$\\Delta$\", fontsize=14)\n",
465 | "plt.ylabel(\"GS\", fontsize=14)\n",
466 | "plt.legend()\n",
467 | "plt.show()"
468 | ],
469 | "execution_count": null,
470 | "outputs": [
471 | {
472 | "output_type": "display_data",
473 | "data": {
474 | "image/png": "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\n",
475 | "text/plain": [
476 | ""
477 | ]
478 | },
479 | "metadata": {
480 | "tags": [],
481 | "needs_background": "light"
482 | }
483 | }
484 | ]
485 | },
486 | {
487 | "cell_type": "markdown",
488 | "metadata": {
489 | "id": "iD9GbO20_i2D"
490 | },
491 | "source": [
492 | "# References\n",
493 | "\n",
494 | "[1] [Cervera-Lierta, Alba, Jakob S. Kottmann, and Alán Aspuru-Guzik. \"The meta-variational quantum eigensolver (meta-vqe): Learning energy profiles of parameterized hamiltonians for quantum simulation.\" arXiv preprint arXiv:2009.13545 (2020)](https://arxiv.org/abs/2009.13545).\n",
495 | "\n",
496 | "[2] [Alba Cervera-Lierta QHACK21 repository](https://github.com/AlbaCL/qhack21/blob/main/Meta-VQE.ipynb)"
497 | ]
498 | }
499 | ]
500 | }
--------------------------------------------------------------------------------
/VQC_Pennylane.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "VQC_Pennylane.ipynb",
7 | "provenance": [],
8 | "collapsed_sections": [],
9 | "authorship_tag": "ABX9TyMEAjRpPKEjSiEYFo0J57oe",
10 | "include_colab_link": true
11 | },
12 | "kernelspec": {
13 | "name": "python3",
14 | "display_name": "Python 3"
15 | }
16 | },
17 | "cells": [
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "id": "view-in-github",
22 | "colab_type": "text"
23 | },
24 | "source": [
25 | "
"
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "metadata": {
31 | "id": "DDnwUeVCa_Dm"
32 | },
33 | "source": [
34 | "# Instlling pennylane in colab\n",
35 | "from IPython.display import clear_output\n",
36 | "! pip install pennylane\n",
37 | "clear_output()"
38 | ],
39 | "execution_count": null,
40 | "outputs": []
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {
45 | "id": "PFoWoOZG6cyF"
46 | },
47 | "source": [
48 | "# Variational Quantum Classifier\n",
49 | "\n",
50 | "# 1) Introduction\n",
51 | "There are several applications for quantum computers, one of the most promising applications is Quantum Machine Learning. \n",
52 | "\n",
53 | "Quantum Machine Learning is a novel field which aims to use Quantum Computer to do machine learning tasks, just as the name states. One of such tasks is the classification problem, where we aim to split the data into different classes. One example of a classification problem is when it is needed to classify if an email is a spam or not, the data would be the email content and we would train examples of spam mails and not spam mails in order to create a model of what a spam mail is and then use this model for novel data to solve our task.\n",
54 | "\n",
55 | "For our example I will talk about the Variational Quantum Classifier which is an Hybrid Quantum-Classical algorithm that is used to classify data. In this demo I will be using [Pennylane](https://pennylane.ai/)."
56 | ]
57 | },
58 | {
59 | "cell_type": "markdown",
60 | "metadata": {
61 | "id": "pajg3Xj-2OST"
62 | },
63 | "source": [
64 | "# 2) Algorithm\n",
65 | "\n",
66 | "The Variational Quantum Classifier (VQC) is consists of three parts: \n",
67 | "\n",
68 | "1. Encoding or Embedding;\n",
69 | "2. Parametrized Quantum Circuit (Ansatz);\n",
70 | "3. Loss Function.\n",
71 | "\n",
72 | "\n",
73 | "\n",
74 | "Image from [Schuld et al.](https://arxiv.org/pdf/1804.00633.pdf)"
75 | ]
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "metadata": {
80 | "id": "IebzaiSK-qoG"
81 | },
82 | "source": [
83 | "## 2.1) Quantum Embedding\n",
84 | "\n",
85 | "Since we are using quantum circuits, we need a way to transform classical data into quantum data, this process is called Quantum Embedding and can be represented as:\n",
86 | "\n",
87 | "$$\n",
88 | "\\vec{x} \\mapsto | \\psi (\\vec{x}) \\rangle\n",
89 | "$$\n",
90 | "\n",
91 | "Here I will present two kinds of quantum embeddings:\n",
92 | "\n",
93 | "- Basis Embedding\n",
94 | "- Amplitude Embedding\n",
95 | "- Angle Embeddding"
96 | ]
97 | },
98 | {
99 | "cell_type": "markdown",
100 | "metadata": {
101 | "id": "dsyKXUFXvelK"
102 | },
103 | "source": [
104 | "### 2.1.1) Basis Embedding\n",
105 | "\n",
106 | "In this kind of embedding, we encode bit strings into quantum states by mapping them to the computational basis. Thus if we have a dataset $\\mathcal{D} = \\{ x^{(1)}, \\dots, x^{(M)} \\}$ with cardinality $M$ we can encode all the dataset into a superposition of computational basis states:\n",
107 | "\n",
108 | "$$\n",
109 | "| \\mathcal{D} \\rangle = \\frac{1}{\\sqrt{M}} \\sum_{m=1}^M | x^{(m)} \\rangle\n",
110 | "$$\n",
111 | "\n",
112 | "As an example we have $\\mathcal{D} = \\{ 00 ,11 \\}$, we can encode this dataset as: \n",
113 | "\n",
114 | "$$\n",
115 | "| \\mathcal{D} \\rangle = \\frac{1}{2} \\big[ |00 \\rangle + | 11 \\rangle \\big]\n",
116 | "$$\n",
117 | "\n",
118 | "This embedding can be done using the pennylane template [`BasisEmbedding`](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.templates.embeddings.BasisEmbedding.html). Let's show one example:"
119 | ]
120 | },
121 | {
122 | "cell_type": "code",
123 | "metadata": {
124 | "colab": {
125 | "base_uri": "https://localhost:8080/"
126 | },
127 | "id": "y_5TpQ0oysu8",
128 | "outputId": "cc412b6f-ec36-4d37-bf63-b3ecea3f411b"
129 | },
130 | "source": [
131 | "import pennylane as qml\n",
132 | "import numpy as np\n",
133 | "\n",
134 | "# Initialize the device\n",
135 | "n_qubits = 2\n",
136 | "dev = qml.device('default.qubit', wires=n_qubits)\n",
137 | "\n",
138 | "@qml.qnode(dev)\n",
139 | "def basis_embedding(data):\n",
140 | " \n",
141 | " # Embedding\n",
142 | " qml.templates.BasisEmbedding(data, wires=range(n_qubits))\n",
143 | "\n",
144 | " return qml.state()\n",
145 | "\n",
146 | "features=np.array([0, 1])\n",
147 | "\n",
148 | "print(f\"Quantum State |01>: {basis_embedding(features)}\")\n",
149 | "\n",
150 | "features=np.array([1,0])\n",
151 | "\n",
152 | "print(f\"Quantum State |10>: {basis_embedding(features)}\")"
153 | ],
154 | "execution_count": null,
155 | "outputs": [
156 | {
157 | "output_type": "stream",
158 | "text": [
159 | "Quantum State |01>: [0.+0.j 1.+0.j 0.+0.j 0.+0.j]\n",
160 | "Quantum State |10>: [0.+0.j 0.+0.j 1.+0.j 0.+0.j]\n"
161 | ],
162 | "name": "stdout"
163 | }
164 | ]
165 | },
166 | {
167 | "cell_type": "markdown",
168 | "metadata": {
169 | "id": "CjAmahNq0Ysn"
170 | },
171 | "source": [
172 | "As we can see in the code, the data is properly encoded into the computational basis. However, this is not a very efficient encoding, mainly because we want to encode continous data and this kind of encoding would be very inneficient to do this."
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "metadata": {
178 | "id": "N8LnJafw01_z"
179 | },
180 | "source": [
181 | "### 2.1.2) Amplitude Embedding\n",
182 | "\n",
183 | "In this encoding we will encode the data into amplitudes of a quantum state. Since all quantum states must be normalized, the first step is to normalize our entry data. Thus a normalized N dimensional datapoint x is represented by the amplitudes of the n-qubit quantum state $| \\psi_x \\rangle$:\n",
184 | "\n",
185 | "$$\n",
186 | "| \\psi_x \\rangle = \\sum_{i=1}^N x_i | i \\rangle\n",
187 | "$$\n",
188 | "\n",
189 | "Where $x_i$ is the i-th element of x and $| i \\rangle$ is the i-th computational basis state.\n",
190 | "\n",
191 | "For instance let's encode the datapoint $x = (0, 1, 4, 0)$ into a quantum state. First the normalized datapoint is $x_{\\mathrm{norm}} = \\frac{1}{\\sqrt{4.123}} ( 0, 1, -4, 0)$, then we can encode into a quantum state:\n",
192 | "\n",
193 | "$$\n",
194 | "| \\psi_x \\rangle = \\frac{1}{\\sqrt{4.123}} \\bigg[ | 01 \\rangle - | 10 \\rangle \\bigg]\n",
195 | "$$\n",
196 | "\n",
197 | "This can be done in pennylane using [`AmplitudeEmbedding`](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.templates.embeddings.AmplitudeEmbedding.html), which encodes a vector of lenght $2^n$ into n qubits."
198 | ]
199 | },
200 | {
201 | "cell_type": "code",
202 | "metadata": {
203 | "colab": {
204 | "base_uri": "https://localhost:8080/"
205 | },
206 | "id": "6Eyu38991mOS",
207 | "outputId": "b05b86eb-9229-48c9-fd21-9528c2096eb9"
208 | },
209 | "source": [
210 | "# Initialize the device\n",
211 | "n_qubits = 2\n",
212 | "dev = qml.device('default.qubit', wires=n_qubits)\n",
213 | "\n",
214 | "@qml.qnode(dev)\n",
215 | "def basis_embedding(data):\n",
216 | " \n",
217 | " # Embedding\n",
218 | " # This will normalize the data for us\n",
219 | " qml.templates.AmplitudeEmbedding(data, wires=range(n_qubits), normalize=True)\n",
220 | "\n",
221 | " return qml.state()\n",
222 | "\n",
223 | "x = np.array([0., 1., 4., 0.])\n",
224 | "x_norm = x/np.linalg.norm(x)\n",
225 | "print(f\"Normalized datapoint: {x_norm}\")\n",
226 | "print(f\"Quantum State: {basis_embedding(x)}\")"
227 | ],
228 | "execution_count": null,
229 | "outputs": [
230 | {
231 | "output_type": "stream",
232 | "text": [
233 | "Normalized datapoint: [0. 0.24253563 0.9701425 0. ]\n",
234 | "Quantum State: [0. +0.j 0.24253563+0.j 0.9701425 +0.j 0. +0.j]\n"
235 | ],
236 | "name": "stdout"
237 | }
238 | ]
239 | },
240 | {
241 | "cell_type": "markdown",
242 | "metadata": {
243 | "id": "LWOLpjmj4FGn"
244 | },
245 | "source": [
246 | "### 2.1.3) Angle Embedding\n",
247 | "\n",
248 | "Another approach is to encode data into qubit rotations, this has the downside of needing n qubits for n-dimensional datapoint, but has the upside of being more easy to implement.\n",
249 | "\n",
250 | "A N-dimensional datapoint $x$ will be represented by N qubits of the form:\n",
251 | "\n",
252 | "$$\n",
253 | " | \\psi_x \\rangle = \\bigotimes_{i=1}^{N} R_j(x_i) | 0 \\rangle\n",
254 | "$$\n",
255 | "\n",
256 | "Where $R_j$ is the rotation on the j-th axis and i can be around the X,Y and Z axis, the $\\bigotimes$ symbol representes that each rotation is independent from each other and act only on one qubit.\n",
257 | "\n",
258 | "This can be done in pennylane using [`AngleEmbedding`](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.templates.embeddings.AngleEmbedding.html)."
259 | ]
260 | },
261 | {
262 | "cell_type": "code",
263 | "metadata": {
264 | "colab": {
265 | "base_uri": "https://localhost:8080/"
266 | },
267 | "id": "nQppqcro51jm",
268 | "outputId": "9efca52d-9267-4747-a318-779cdac2dff4"
269 | },
270 | "source": [
271 | "# Initialize the device\n",
272 | "n_qubits = 2\n",
273 | "dev = qml.device('default.qubit', wires=n_qubits)\n",
274 | "\n",
275 | "@qml.qnode(dev)\n",
276 | "def basis_embedding(data):\n",
277 | " \n",
278 | " # Embedding\n",
279 | " # This will normalize the data for us\n",
280 | " qml.templates.AngleEmbedding(data, wires=range(n_qubits))\n",
281 | "\n",
282 | " return qml.state()\n",
283 | "\n",
284 | "x = np.array([.5, 1.2])\n",
285 | "\n",
286 | "print(f\"Quantum State: {basis_embedding(x)}\\n\")\n",
287 | "print(basis_embedding.draw())"
288 | ],
289 | "execution_count": null,
290 | "outputs": [
291 | {
292 | "output_type": "stream",
293 | "text": [
294 | "Quantum State: [ 0.79967793+0.j 0. -0.54708911j 0. -0.2041913j\n",
295 | " -0.13969478+0.j ]\n",
296 | "\n",
297 | " 0: ──RX(0.5)──╭┤ State \n",
298 | " 1: ──RX(1.2)──╰┤ State \n",
299 | "\n"
300 | ],
301 | "name": "stdout"
302 | }
303 | ]
304 | },
305 | {
306 | "cell_type": "markdown",
307 | "metadata": {
308 | "id": "7mFzwKht6TYk"
309 | },
310 | "source": [
311 | "## 2.2) Variational Circuit\n",
312 | "\n",
313 | "After encoding the data we need to create our proper Quantum Neural Network which will consist of a parametrized quantum circuit. The construction of this circuit can be done in plethora of ways which can be seen in the pennylane [layers templates page](https://pennylane.readthedocs.io/en/stable/introduction/templates.html) for instance. \n",
314 | "\n",
315 | "The main idea is to construct a parametrized unitary $U(\\theta)$ which we will tune the parameters in order to tell us from which class the data belongs."
316 | ]
317 | },
318 | {
319 | "cell_type": "markdown",
320 | "metadata": {
321 | "id": "8QDO9y6p8ZZV"
322 | },
323 | "source": [
324 | "## 2.3) Loss Function\n",
325 | "\n",
326 | "The loss function is the function that we want to minimize in order to solve the task that we want. There are several loss functions that can be used and will further explained in future posts. "
327 | ]
328 | },
329 | {
330 | "cell_type": "markdown",
331 | "metadata": {
332 | "id": "rSBRwZ0BugZt"
333 | },
334 | "source": [
335 | "# 3) Using VQC for the iris dataset\n",
336 | "\n",
337 | "Now I will implement the Variational Quantum Classifier for the famous [Iris dataset](https://archive.ics.uci.edu/ml/datasets/iris). The goal for this dataset is to classify the class of iris plant using attributes of the plant."
338 | ]
339 | },
340 | {
341 | "cell_type": "code",
342 | "metadata": {
343 | "id": "zjqOv46b9-9m"
344 | },
345 | "source": [
346 | "import sys\n",
347 | "import pennylane as qml\n",
348 | "import numpy as np\n",
349 | "from sklearn import datasets\n",
350 | "from sklearn.model_selection import train_test_split\n",
351 | "from tqdm import tqdm"
352 | ],
353 | "execution_count": null,
354 | "outputs": []
355 | },
356 | {
357 | "cell_type": "markdown",
358 | "metadata": {
359 | "id": "3MFE1t_bB4WN"
360 | },
361 | "source": [
362 | "## 3.1) Loading the dataset\n",
363 | "\n",
364 | "We load the Iris dataset from sklearn datasets and split into a training and validation split as usual.."
365 | ]
366 | },
367 | {
368 | "cell_type": "code",
369 | "metadata": {
370 | "id": "3OrPb1PU1wD1"
371 | },
372 | "source": [
373 | "iris = datasets.load_iris()\n",
374 | "X = iris.data[:, :]\n",
375 | "Y = iris.target\n",
376 | "\n",
377 | "X_train, X_valid, Y_train, Y_valid = train_test_split(X, Y, test_size=0.25, random_state=42)"
378 | ],
379 | "execution_count": null,
380 | "outputs": []
381 | },
382 | {
383 | "cell_type": "markdown",
384 | "metadata": {
385 | "id": "4jba2Nc2B8Jn"
386 | },
387 | "source": [
388 | "## 3.2) Constructing the Variational Circuit\n",
389 | "\n",
390 | "In order to construct the variational circuit, we need three steps:\n",
391 | "- Embed the data into a quantum state: Which II use the angle embedding;\n",
392 | "- Create a Parametetrized Quantum Circuit (PQC): Which I will use the Strongly Entangling Layers Template;\n",
393 | "- Measure the qubits: Since we are classifying three classes, I will measure all three qubits using the expectation Z value on each one."
394 | ]
395 | },
396 | {
397 | "cell_type": "code",
398 | "metadata": {
399 | "id": "UCRaTKkhCBjr"
400 | },
401 | "source": [
402 | "n_qubits = X_train.shape[1]\n",
403 | "dev = qml.device('default.qubit', wires=n_qubits)\n",
404 | "\n",
405 | "@qml.qnode(dev)\n",
406 | "def circuit(weights, data):\n",
407 | " # Embedding\n",
408 | " qml.templates.AngleEmbedding(data, wires=range(n_qubits))\n",
409 | " \n",
410 | " # Create Parametrized layer\n",
411 | " qml.templates.StronglyEntanglingLayers(weights, wires=range(n_qubits))\n",
412 | "\n",
413 | " return [qml.expval(qml.PauliZ(0)) ,qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2))]"
414 | ],
415 | "execution_count": null,
416 | "outputs": []
417 | },
418 | {
419 | "cell_type": "markdown",
420 | "metadata": {
421 | "id": "ZnDXSGSYCCI-"
422 | },
423 | "source": [
424 | "## 3.2) Defining the cost function\n",
425 | "\n",
426 | "In order to use the mean squared error loss function it is need to one hot encode each label into a vector and then use each expectation value from the circuit to approximate each class."
427 | ]
428 | },
429 | {
430 | "cell_type": "code",
431 | "metadata": {
432 | "id": "PVAKzDNOCKGA"
433 | },
434 | "source": [
435 | "def cost(weights, x, y):\n",
436 | " \"\"\" Define the cost function for the classification task.\n",
437 | " \"\"\"\n",
438 | " epoch_loss = []\n",
439 | " label2vec = {\n",
440 | " 0: [1, 0, 0],\n",
441 | " 1: [0, 1, 0],\n",
442 | " 2: [0, 0, 1]\n",
443 | " }\n",
444 | " \n",
445 | " for x_data, y_data in zip(x,y):\n",
446 | " c = circuit(weights, x_data)\n",
447 | " label = label2vec[y_data]\n",
448 | " c, label = np.array(c),np.array(label)\n",
449 | " s = np.sum(abs(c - label)**2)\n",
450 | " \n",
451 | " epoch_loss.append(s)\n",
452 | " \n",
453 | " return np.sum(epoch_loss) / len(epoch_loss)\n",
454 | "\n",
455 | "# Define the accuracy\n",
456 | "accuracy = lambda x,y: np.sum(x == y) / len(x)\n",
457 | "\n",
458 | "def iterate_minibatches(inputs, targets, batch_size):\n",
459 | " \"\"\" A generator for batches of the input data\n",
460 | " \"\"\"\n",
461 | " for start_idx in range(0, inputs.shape[0] - batch_size + 1, batch_size):\n",
462 | " idxs = slice(start_idx, start_idx + batch_size)\n",
463 | " yield inputs[idxs], targets[idxs]"
464 | ],
465 | "execution_count": null,
466 | "outputs": []
467 | },
468 | {
469 | "cell_type": "markdown",
470 | "metadata": {
471 | "id": "6eXy9Faq7QYt"
472 | },
473 | "source": [
474 | "## 3.5) Training\n",
475 | "\n",
476 | "In order to train we need to define hyperparameters, weight initialization and the optimizer.\n",
477 | "\n",
478 | "- Optimizer: I choose to use Adam;\n",
479 | "\n",
480 | "- Intialization: I choose to initialize with a random uniform distribution of angles, even though this has been show to present barren plateaus on the loss landscape, maybe in a future post I will talk about mitigating barren plateaus;\n",
481 | "\n",
482 | "- Hyperparameters: I choose 2 layers for the PQC, and a learning rate of 0.1;"
483 | ]
484 | },
485 | {
486 | "cell_type": "code",
487 | "metadata": {
488 | "id": "J7PgEp3B4cia"
489 | },
490 | "source": [
491 | "# Hyperparameters\n",
492 | "layers = 2\n",
493 | "learning_rate = 0.05\n",
494 | "epochs = 10\n",
495 | "batch_size = 10\n",
496 | "\n",
497 | "# Optimizer\n",
498 | "opt = qml.AdamOptimizer(learning_rate)#, beta1=0.9, beta2=0.999)\n",
499 | "\n",
500 | "# Initialize Random Weights\n",
501 | "params = np.random.uniform(low= 0, high= np.pi, size=(layers, n_qubits, 3))\n",
502 | "\n",
503 | "# Helpers\n",
504 | "val_acc = []\n",
505 | "t_acc = []\n",
506 | "t_loss = []"
507 | ],
508 | "execution_count": null,
509 | "outputs": []
510 | },
511 | {
512 | "cell_type": "code",
513 | "metadata": {
514 | "colab": {
515 | "base_uri": "https://localhost:8080/"
516 | },
517 | "id": "XVfbD8H5CVNz",
518 | "outputId": "f5892e1f-af2e-41b7-882a-2f646a457118"
519 | },
520 | "source": [
521 | "# Training\n",
522 | "for i in tqdm(range(epochs)):\n",
523 | " train_acc = []\n",
524 | " for Xbatch, Ybatch in iterate_minibatches(X_train, Y_train, batch_size=batch_size):\n",
525 | " params = opt.step(lambda v: cost(v, Xbatch, Ybatch), params)\n",
526 | " \n",
527 | " \n",
528 | " train_predictions = []\n",
529 | " for x in X_train:\n",
530 | " pred = circuit(params, x)\n",
531 | " label = np.argmax(pred)\n",
532 | " train_predictions.append(label)\n",
533 | " \n",
534 | " train_acc = accuracy(train_predictions,Y_train)\n",
535 | " t_acc.append(train_acc) \n",
536 | " \n",
537 | " valid_predictions = []\n",
538 | " for x in X_valid:\n",
539 | " pred = circuit(params, x)\n",
540 | " label = np.argmax(pred)\n",
541 | " valid_predictions.append(label)\n",
542 | " \n",
543 | " valid_acc = accuracy(valid_predictions,Y_valid)\n",
544 | " val_acc.append(valid_acc)\n",
545 | "\n",
546 | " loss = np.mean(cost(params, X_train, Y_train))\n",
547 | " t_loss.append(loss)"
548 | ],
549 | "execution_count": null,
550 | "outputs": [
551 | {
552 | "output_type": "stream",
553 | "text": [
554 | "100%|██████████| 10/10 [00:41<00:00, 4.16s/it]\n"
555 | ],
556 | "name": "stderr"
557 | }
558 | ]
559 | },
560 | {
561 | "cell_type": "code",
562 | "metadata": {
563 | "colab": {
564 | "base_uri": "https://localhost:8080/",
565 | "height": 335
566 | },
567 | "id": "3zMPLqNOBZc1",
568 | "outputId": "7e66ad86-1a1d-4bee-ef99-5bddc4474caa"
569 | },
570 | "source": [
571 | "import matplotlib.pyplot as plt\n",
572 | "\n",
573 | "epochs = range(len(t_loss))\n",
574 | "\n",
575 | "\n",
576 | "fig = plt.figure(figsize=(14,5))\n",
577 | "gs = fig.add_gridspec(1, 2)\n",
578 | "ax1 = fig.add_subplot(gs[0, 0])\n",
579 | "ax2 = fig.add_subplot(gs[0, 1])\n",
580 | "\n",
581 | "ax1.plot(epochs, t_loss)\n",
582 | "ax1.set_xlabel('Epochs')\n",
583 | "ax1.set_ylabel('Loss')\n",
584 | "\n",
585 | "ax2.plot(epochs, t_acc, label='Train')\n",
586 | "ax2.plot(epochs, val_acc, label='Validation')\n",
587 | "ax2.set_xlabel('Epochs')\n",
588 | "ax2.set_ylabel('Accuracy')\n",
589 | "plt.legend()\n",
590 | "plt.show()"
591 | ],
592 | "execution_count": null,
593 | "outputs": [
594 | {
595 | "output_type": "display_data",
596 | "data": {
597 | "image/png": "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\n",
598 | "text/plain": [
599 | ""
600 | ]
601 | },
602 | "metadata": {
603 | "tags": [],
604 | "needs_background": "light"
605 | }
606 | }
607 | ]
608 | },
609 | {
610 | "cell_type": "markdown",
611 | "metadata": {
612 | "id": "mTFKf2rSv1-a"
613 | },
614 | "source": [
615 | "# References\n",
616 | "\n",
617 | "- [Variational Classifier Pennylane Demo](https://pennylane.ai/qml/demos/tutorial_variational_classifier.html)\n",
618 | "\n",
619 | "- [Quantum Embedding](https://pennylane.ai/qml/glossary/quantum_embedding.html)\n",
620 | "\n",
621 | "- [Circuit-Centric quantum classifiers](https://arxiv.org/abs/1804.00633)"
622 | ]
623 | }
624 | ]
625 | }
--------------------------------------------------------------------------------