{ "cells": [ { "cell_type": "markdown", "id": "b827504d", "metadata": {}, "source": [ "# Paralelization for gradient-free optimizers: Differential Evolution\n", "In this notebook, a more complex example is aimed. Here, a differential evolution optimization will be performed employing the vQPUs as accelerators of the quantum task of which the parameters are updated.\n", "\n", "As in the rest of the examples, all the imports are done and the vQPUs are raised. After this, they are brought to the program workflow in form of `QPU` instances." ] }, { "cell_type": "code", "execution_count": 1, "id": "c532632a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requested QPUs with command:\n", "\tqraise -n 10 -t 00:20:00 --simulator=Aer --co-located\n", "QPUs ready to work ✅\n", "QPU 377561_2384905, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384906, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384907, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384908, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384909, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384910, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384911, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384912, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384913, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n", "QPU 377561_2384914, backend: SimpleBackend, simulator: AerSimulator, version: 0.0.1.\n" ] } ], "source": [ "import os, sys\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import random\n", "import math\n", "from scipy.stats import entropy, norm\n", "from scipy.optimize import differential_evolution\n", "\n", "sys.path.append(os.getenv(\"HOME\"))\n", "\n", "from cunqa.circuit import CunqaCircuit\n", "from cunqa import get_QPUs, qraise, qdrop\n", "from cunqa.qpu import run \n", "from cunqa.mappers import QJobMapper, QPUCircuitMapper\n", "\n", "family = qraise(10, \"00:20:00\", simulator = \"Aer\", co_located = True)\n", "qpus = get_QPUs(co_located = True, family = family)\n", "\n", "for q in qpus:\n", " print(f\"QPU {q.id}, backend: {q.backend['name']}, simulator: {q.backend['simulator']}, version: {q.backend['version']}.\")" ] }, { "cell_type": "markdown", "id": "1a4f94f9", "metadata": {}, "source": [ "In this example, the same ansatz, target distribution and distribution divergence measure is defined as in the parameters update feature example. " ] }, { "cell_type": "code", "execution_count": 2, "id": "723a8b1b", "metadata": {}, "outputs": [], "source": [ "def hardware_efficient_ansatz(num_qubits, num_layers):\n", " qc = CunqaCircuit(num_qubits)\n", " for p_id in range(num_layers):\n", " for qubit in range(num_qubits):\n", " qc.ry(f\"phi_{p_id}_{qubit}\", qubit)\n", " qc.rz(f\"lam_{p_id}_{qubit}\", qubit)\n", " for qubit in range(num_qubits - 1):\n", " qc.cx(qubit, qubit + 1)\n", " qc.measure_all()\n", " return qc\n", "\n", "def target_distribution(num_qubits):\n", " # Define a normal distribution over the states\n", " num_states = 2 ** num_qubits\n", " states = np.arange(num_states)\n", " mean = num_states / 2\n", " std_dev = num_states / 4\n", " target_probs = norm.pdf(states, mean, std_dev)\n", " target_probs /= target_probs.sum() # Normalize to make it a valid probability distribution\n", " target_dist = {format(i, f'0{num_qubits}b'): target_probs[i] for i in range(num_states)}\n", " return target_dist\n", "\n", "def KL_divergence(counts, n_shots, target_dist):\n", " # Convert counts to probabilities\n", " pdf = pd.DataFrame.from_dict(counts, orient=\"index\").reset_index()\n", " pdf.rename(columns={\"index\": \"state\", 0: \"counts\"}, inplace=True)\n", " pdf[\"probability\"] = pdf[\"counts\"] / n_shots\n", " \n", " # Create a dictionary for the obtained distribution\n", " obtained_dist = pdf.set_index(\"state\")[\"probability\"].to_dict()\n", " \n", " # Ensure all states are present in the obtained distribution\n", " for state in target_dist:\n", " if state not in obtained_dist:\n", " obtained_dist[state] = 0.0\n", " \n", " # Convert distributions to lists for KL divergence calculation\n", " target_probs = [target_dist[state] for state in sorted(target_dist)]\n", " obtained_probs = [obtained_dist[state] for state in sorted(obtained_dist)]\n", " \n", " # Calculate KL divergence\n", " kl_divergence = entropy(obtained_probs, target_probs)\n", " \n", " return kl_divergence\n", "\n", "num_qubits = 6\n", "num_layers = 3\n", "ansatz = hardware_efficient_ansatz(num_qubits, num_layers)\n", "num_params = 2 * num_qubits * num_layers\n", "\n", "pop = [\n", " [random.uniform(-math.pi, math.pi) for _ in range(num_params)]\n", " for _ in range(num_params)\n", "]\n", "bounds=[(-np.pi,np.pi) for _ in range(num_params)]" ] }, { "cell_type": "markdown", "id": "a20e9a95-dd53-48a6-bd12-27775bc8ee55", "metadata": {}, "source": [ "What changes is the cost function, that in this case will be the following." ] }, { "cell_type": "code", "execution_count": 3, "id": "2118dbd4", "metadata": {}, "outputs": [], "source": [ "def cost_function(result):\n", " target_dist = target_distribution(num_qubits)\n", " counts = result.counts\n", " \n", " return KL_divergence(counts, 1000, target_dist)" ] }, { "cell_type": "markdown", "id": "d63ecb50-d796-48c7-939e-24c396b7f9f1", "metadata": {}, "source": [ "Now, similar to the previous example, a `make_callback` function is defined in order to generate a callback function tailored to the mapper employed." ] }, { "cell_type": "code", "execution_count": 4, "id": "5e89e8e8-2d80-4f4c-ae18-bba1fad6dbb7", "metadata": {}, "outputs": [], "source": [ "def make_callback(mapper):\n", " i = 0\n", " \n", " best_individual = []\n", " energies = []\n", " def callback(xk, convergence = 1e-8):\n", " nonlocal i\n", " best_individual.append(xk)\n", " energy = mapper(cost_function, [xk])[0]\n", " energies.append(energy)\n", " i += 1\n", "\n", " return callback, energies" ] }, { "cell_type": "markdown", "id": "0629a4c6", "metadata": {}, "source": [ "## QJobMapper\n", "After all the previous steps, the optimization is ready to be performed. In this case, the mapper employed will be `QJobMapper`. This mapper takes a set of `QJobs` and uses the parameter update feature in each iteration." ] }, { "cell_type": "code", "execution_count": 5, "id": "27d6e57f", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n", "\u001b[33m\twarning: [qjob.py] You have not obtained the previous results. They will be discarded.\n", "\u001b[0m\n" ] } ], "source": [ "init_qjobs = []\n", "for i in range(1 * num_params): # we set pop=1 as the population size is pop*num_params\n", " qpu = qpus[i%len(qpus)] # we select the qpu\n", " init_qjobs.append(run(ansatz, qpu, [0.0 for _ in range(num_params)], shots=1000))\n", "\n", "mapper = QJobMapper(init_qjobs)\n", "\n", "callback1, energies1 = make_callback(mapper)\n", "result1 = differential_evolution(cost_function, \n", " bounds, \n", " maxiter = 1000, \n", " workers = mapper, \n", " updating = 'deferred',\n", " strategy = 'best1bin',\n", " init = pop, \n", " polish = False, \n", " callback=callback1\n", " )" ] }, { "cell_type": "code", "execution_count": 6, "id": "de3f29a4-162f-45d8-9c70-06edfd4a886e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " message: Maximum number of iterations has been exceeded.\n", " success: False\n", " fun: 0.20667795823664736\n", " x: [-1.802e-01 -1.348e+00 ... -1.992e+00 3.043e-01]\n", " nit: 1000\n", " nfev: 36036\n", " population: [[-1.802e-01 -1.348e+00 ... -1.992e+00 3.043e-01]\n", " [ 1.073e+00 -1.708e+00 ... -1.075e+00 9.150e-01]\n", " ...\n", " [ 1.922e+00 7.773e-01 ... -1.187e+00 -1.805e+00]\n", " [-7.132e-01 4.087e-01 ... -1.815e+00 2.353e-01]]\n", " population_energies: [ 2.067e-01 2.392e-01 ... 2.599e-01 2.871e-01]\n" ] } ], "source": [ "print(result1)" ] }, { "cell_type": "markdown", "id": "abd7a7e6", "metadata": {}, "source": [ "## QPUCircuitMapper\n", "In this case, instead of using the parameter update feature, it creates a circuit each time the mapper is called. But, as it can be seen, the employment of this mapper and the previous one is analogous." ] }, { "cell_type": "code", "execution_count": 7, "id": "b1b08cf9", "metadata": {}, "outputs": [], "source": [ "mapper = QPUCircuitMapper(qpus, ansatz, shots=1000)\n", "\n", "callback2, energies2 = make_callback(mapper)\n", "result2 = differential_evolution(cost_function, \n", " bounds, \n", " maxiter = 1000, \n", " workers = mapper, \n", " updating = 'deferred',\n", " strategy = 'best1bin', \n", " init = pop, \n", " polish = False, \n", " callback=callback2\n", " )" ] }, { "cell_type": "code", "execution_count": 8, "id": "dfdeb42c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " message: Maximum number of iterations has been exceeded.\n", " success: False\n", " fun: 0.15722812798087116\n", " x: [ 2.837e-01 -2.515e+00 ... 1.825e+00 2.464e+00]\n", " nit: 1000\n", " nfev: 36036\n", " population: [[ 2.837e-01 -2.515e+00 ... 1.825e+00 2.464e+00]\n", " [-1.629e-01 -2.074e+00 ... 1.720e+00 1.163e+00]\n", " ...\n", " [-5.258e-01 -3.135e+00 ... 1.901e+00 2.088e+00]\n", " [-1.350e+00 -2.727e+00 ... 1.458e+00 2.012e+00]]\n", " population_energies: [ 1.572e-01 2.595e-01 ... 2.511e-01 2.468e-01]\n" ] } ], "source": [ "print(result2)" ] }, { "cell_type": "markdown", "id": "a3a35a95-de53-4cc3-80b5-e8ab5a5d0ceb", "metadata": {}, "source": [ "### Comparison between QJobMapper and QPUCircuitMapper" ] }, { "cell_type": "code", "execution_count": 9, "id": "bbaf8976", "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Removed job(s) with ID(s): \u001b[1;32m377561 \u001b[0m\n" ] } ], "source": [ "%matplotlib inline\n", "plt.clf()\n", "plt.plot(np.linspace(0, result1.nit, result1.nit), energies1, label=\"Optimization path (QJobMapper)\")\n", "upper_bound = result1.nit\n", "plt.plot(np.linspace(0, result2.nit, result2.nit), energies2, label=\"Optimization path (QPUCircuitMapper)\")\n", "plt.plot(np.linspace(0, upper_bound, upper_bound), np.zeros(upper_bound), \"--\", label=\"Target cost\")\n", "plt.xlabel(\"Step\"); plt.ylabel(\"Cost\");\n", "plt.legend(loc=\"upper right\");\n", "plt.title(f\"n = {num_qubits}, l = {num_layers}, # params = {num_params}\")\n", "plt.grid(True)\n", "plt.show()\n", "\n", "qdrop(family)" ] }, { "cell_type": "code", "execution_count": null, "id": "a75d387d-eb7d-4998-8cce-c27cccd32dcc", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 10, "id": "b9030814-d1ff-408b-aee2-45247e26dfed", "metadata": {}, "outputs": [], "source": [ "# TODO: Paralelization of expectation value terms" ] }, { "cell_type": "code", "execution_count": 11, "id": "0ff7f237", "metadata": {}, "outputs": [], "source": [ "# TODO: Paralelization for gradient optimizers" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.10" } }, "nbformat": 4, "nbformat_minor": 5 }