{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "c532632a", "metadata": {}, "outputs": [], "source": [ "import os, sys\n", "\n", "# path to access c++ files\n", "sys.path.append(os.getenv(\"HOME\"))" ] }, { "cell_type": "code", "execution_count": 2, "id": "c2d0c54e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "QPU 0, backend: SimpleSimulator, simulator: SimpleMunich, version: 0.0.1.\n", "QPU 1, backend: SimpleSimulator, simulator: SimpleMunich, version: 0.0.1.\n", "QPU 2, backend: SimpleSimulator, simulator: SimpleMunich, version: 0.0.1.\n" ] } ], "source": [ "from cunqa import getQPUs\n", "\n", "qpus = getQPUs(local=False)\n", "\n", "for q in qpus:\n", " print(f\"QPU {q.id}, backend: {q.backend.name}, simulator: {q.backend.simulator}, version: {q.backend.version}.\")\n" ] }, { "cell_type": "markdown", "id": "5db7e8c6", "metadata": {}, "source": [ "# Examples for optimizations" ] }, { "cell_type": "markdown", "id": "4faa81e1", "metadata": {}, "source": [ "Before sending a circuit to the QClient, a transpilation process occurs (if not, it is done by the user). This process, in some cases, can take much time and resources, in addition to the sending cost itself. If we were to execute a single circuit once, it shouldn´t be a big problem, but it is when it comes to variational algorithms.\n", "\n", "This quantum-classical algorithms require several executions of the same circuit but changing the value of the parameters, which are optimized in the classical part. In order to optimize this, we developed a functionallity that allows the user to upgrade the circuit parameters with no extra transpilations of the circuit, sending to the `QClient` the list of the parameters **ONLY**. This is of much advantage to speed up the computation in the cases in which transpilation takes a significant part of the total time of the simulation.\n", "\n", "Let´s see how to work with this feature taking as an example a _Variational Quantum Algorithm_ for state preparation.\n", "\n", "We start from a _Hardware Efficient Ansatz_ to build our parametrized circuit:" ] }, { "cell_type": "code", "execution_count": 3, "id": "b0a68703", "metadata": {}, "outputs": [], "source": [ "from qiskit import QuantumCircuit\n", "from qiskit.circuit import Parameter\n", "\n", "def hardware_efficient_ansatz(num_qubits, num_layers):\n", " qc = QuantumCircuit(num_qubits)\n", " param_idx = 0\n", " for _ in range(num_layers):\n", " for qubit in range(num_qubits):\n", " phi = Parameter(f'phi_{param_idx}_{qubit}')\n", " lam = Parameter(f'lam_{param_idx}_{qubit}')\n", " qc.ry(phi, qubit)\n", " qc.rz(lam, qubit)\n", " param_idx += 1\n", " for qubit in range(num_qubits - 1):\n", " qc.cx(qubit, qubit + 1)\n", " qc.measure_all()\n", " return qc" ] }, { "cell_type": "markdown", "id": "6b9f1aa6", "metadata": {}, "source": [ "The we need a cost function. We will define a target distribution and measure how far we are from it. We choose to prepare a normal distribution among all the $2^n$ possible outcomes of the circuit." ] }, { "cell_type": "code", "execution_count": 4, "id": "4e4b9c38", "metadata": {}, "outputs": [], "source": [ "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", "import pandas as pd\n", "from scipy.stats import entropy, norm\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", " " ] }, { "cell_type": "code", "execution_count": 5, "id": "c74e1438", "metadata": {}, "outputs": [], "source": [ "num_qubits = 6\n", "\n", "num_layers = 3\n", "\n", "n_shots = 1e5" ] }, { "cell_type": "markdown", "id": "d23f13db", "metadata": {}, "source": [ "### Simply using the `QPU.run()` method" ] }, { "cell_type": "markdown", "id": "fa26a3eb", "metadata": {}, "source": [ "At first we should try the intiutive alternative: upgrading parameters at the QClient, transpiling and sending the whole circuit to the QPU." ] }, { "cell_type": "code", "execution_count": 6, "id": "5398907b", "metadata": {}, "outputs": [], "source": [ "def cost_function_run(params):\n", " n_shots = 1e5\n", " target_dist = target_distribution(num_qubits)\n", " \n", " circuit = ansatz.assign_parameters(params)\n", " \n", " result = qpu.run(circuit, transpile = True, opt_level = 0, shots = n_shots).result\n", " \n", " counts = result.counts\n", " \n", " return KL_divergence(counts, n_shots, target_dist)" ] }, { "cell_type": "markdown", "id": "d3ec8a31", "metadata": {}, "source": [ "Our cost function updates the parameters given by the optimizer, asigns them to the ansatz and sends the circuit with the transpilation option set `True`. Let´s choose a QPU to work with and go ahead with the optimization:" ] }, { "cell_type": "code", "execution_count": 7, "id": "6e2d80b8", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import time\n", "\n", "qpu = qpus[0]" ] }, { "cell_type": "code", "execution_count": 8, "id": "052a5b83", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Iteration step 0: f(x) = 5.644885693319627\n", "Iteration step 20: f(x) = 3.2489683099416027\n", "Iteration step 40: f(x) = 0.700518505677799\n", "Iteration step 60: f(x) = 0.5086090832144972\n", "Iteration step 80: f(x) = 0.43838720938090936\n", "Iteration step 100: f(x) = 0.3712628503801477\n", "Iteration step 120: f(x) = 0.26705681526906927\n", "Iteration step 140: f(x) = 0.19667531489860712\n", "Iteration step 160: f(x) = 0.1557449733702997\n", "Iteration step 180: f(x) = 0.12627009394865418\n", "Iteration step 200: f(x) = 0.09875260103360035\n", "Iteration step 220: f(x) = 0.0796950348124891\n", "Iteration step 240: f(x) = 0.08664296063303309\n", "Iteration step 260: f(x) = 0.07786113607509895\n", "Iteration step 280: f(x) = 0.07138004921940413\n", "Iteration step 300: f(x) = 0.06928285629833203\n", "Iteration step 320: f(x) = 0.06625305423998329\n", "Iteration step 340: f(x) = 0.06791448821860378\n", "Iteration step 360: f(x) = 0.06584292813374018\n", "Iteration step 380: f(x) = 0.06878749095657292\n", "\n", " Normal return from subroutine COBYLA\n", "\n", " NFVALS = 391 F = 6.630927E-02 MAXCV = 0.000000E+00\n", " X = 2.483621E-01 -4.507945E-01 4.142488E-02 1.693378E-01 9.101863E-01\n", " 7.824475E-02 2.095159E-01 3.357104E-01 -1.846450E-01 5.492609E-01\n", " 6.121254E-01 5.378724E-01 6.303580E-01 -2.833794E-01 -9.201173E-02\n", " 4.659155E-01 2.156028E-01 -1.392999E+00 8.187967E-02 4.551563E-01\n", " 1.093445E+00 5.679521E-01 1.143250E+00 6.251608E-01 1.570452E+00\n", " -3.110023E-01 7.930667E-01 -3.996242E-01 8.965306E-01 -1.083956E-01\n", " -4.277121E-01 1.343497E+00 -7.796074E-02 1.690255E+00 -1.356059E-01\n", " 1.691876E+00\n", "\n", "Total optimization time: 28.403029203414917 s\n", "\n" ] } ], "source": [ "ansatz = hardware_efficient_ansatz(num_qubits, num_layers)\n", "\n", "num_parameters = ansatz.num_parameters\n", "\n", "initial_parameters = np.zeros(num_parameters)\n", "\n", "from scipy.optimize import minimize\n", "\n", "i = 0\n", "\n", "cost_run = []\n", "individuals_run = []\n", "\n", "def callback(xk):\n", " global i\n", " e = cost_function_run(xk)\n", " individuals_run.append(xk)\n", " cost_run.append(e)\n", " if i%20 == 0:\n", " print(f\"Iteration step {i}: f(x) = {e}\")\n", " i+=1\n", "\n", "tick = time.time()\n", "optimization_result_run = minimize(cost_function_run, initial_parameters, method='COBYLA',\n", " callback=callback, tol = 0.01,\n", " options={\n", " 'disp': True, # Print info at the end\n", " 'maxiter': 4000 # Limit the number of iterations\n", " })\n", "tack = time.time()\n", "time_run = tack-tick\n", "print()\n", "print(\"Total optimization time: \", time_run, \" s\")\n", "print()" ] }, { "cell_type": "code", "execution_count": 9, "id": "bcc4cf95", "metadata": {}, "outputs": [ { "data": { "image/png": "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JaWpw6iSxhabvLw8kpOTycnJYffu3fTq1ate+2t3jVL1UFxcTNu2bZtlglfOJSKkpqZSXFxc7301yStVT5rgVVOI9X3niO6a5z7dxOatJSwv2Viv/UaensqoPpVv0qOUUs7hiJb8S59t4d9bSvlT9uY6P56bs5nH/9M8L7QoVZPdu3dz1VVX0bdvX3r37s0999xDSUlJjfvk5ubywgsvRJ/v3buXiRMn1qveRx55hNmzZ9c73nfffZd169ad9HEawm9+85vo8vbt2xk4sG73Y3nmmWeYNm1ag8YyceJEtm7dCsCFF17IkSPWrQtKSko499xzCYVCDVNRTd+UauxHY37j9YfTl5lxT9Vvn1i15G/TNaXmGNu6devMsWPHmqz+SCRihg0bZqZMmWKMMSYUCpnbb7/d3H///cYYU21s27ZtM2eddVajxVnerbfeambOnNmk561MUlJSdLn8OakpttLSUpOenm5KS0urLIvFmjVrzPjx46PPp06dah5//PHo80cffdS89tprJ8S2bt26E46FfuO1aj6Pi5JQlXdCU6rZmjNnDoFAgNtuuw0At9vNH//4R6ZMmUJhYSHTp0/nqquuYuzYsfTt25fHHnsMgAcffJAtW7aQmZnJAw88UKEVO3XqVMaPH89FF11Ez549ef7553n66acZPHgwI0eO5PDhwwBMnjyZN998k6VLl5KZmUlmZibp6enRvuKXX36ZYcOGMWjQICZMmEBhYSELFizg/fff54EHHmD06NFs2bIlehyATz/9lMGDB5Oens7tt99OMBgEoGfPnvziF79gyJAhpKens2HDhhPOxdSpU6t8rQDjx48nKyuLs846i7/+9a/Rc1BUVERmZiY33mjdICscDnPHHXcwfPhwLr74YoqKik6oZ86cOQwZMgSPx+rdHjt2LPfeey9Dhw7l2WefrfB6AJKTrbsozp07l7FjxzJx4kQGDBjAjTfeiLFn/S37PZW58soref311yvEP3369Lq8JWrliD75WPg1yauT9Ni/17Ju77EGPeaZp7bhF9+u/va2a9euJSsrq8K6Nm3acNppp7F5s3XPksWLF7NmzRoSExMZNmwYl19+OU888QRr1qxh5cqVgNVVUd6aNWtYsWIFxcXF9OnTh9/97nesWLGC++67j2nTpnHvvfdGtx06dGj0OA888ACXXnopAFdffTV33GHd+e/hhx/m73//Oz/60Y+48sorueKKK7jkkksqDFMsLi5m8uTJfPrpp/Tr149bbrmFF198MVpXx44dWb58OS+88AJPPfUUf/vb3044H1W91qFDhzJlyhQ6dOhAUVERw4YNY8KECTzxxBM8//zzFc7Bpk2beP3113n66af57ne/y1tvvcVNN91UoY758+efcM5LSkpYunQpYP3zq86KFStYu3Ytp556KqNHj2b+/Pmcc845zJ8/n+uvvz66Xfv27QkGg+Tk5JCamsrAgQNZsmRJtcetj9bbkne7CGqSVw500UUXkZqaSkJCAldffTVffPFFrfuMGzeOlJQUOnXqRNu2bfn2t78NQHp6+gn/EMq88cYbLF++nCeeeAKw/lGMGTOG9PR0pk+fztq1a2usc+PGjfTq1Yt+/foBcOuttzJv3rxo+dVXXw1AVlZWtTFU91qfe+45Bg0axMiRI9m1axebNm2qcv9evXqRmZlZYz379u2jU6eKkzxee+21Nb62MsOHD6dbt264XC4yMzOjx6/qmJ07d2bvXut2xG63G5/PR15eXp3qqUmrbclrd406WTW1uOPlzDPPrNA1AHDs2DF27txJnz59WLBgwQlD7eoy9M7v90eXXS5X9LnL5aryAuCaNWt49NFHmTdvHm63G7BatO+++y6DBg1i6tSpzJ07t74vr8qY3G53tRchq3qtc+fOZfbs2SxcuJDExETGjh1b7fjy8q/b7XZX2V2TkJBwwv7lp/71eDxEIlYuiUQiFS6CVz5+2euo6pjFxcUkJCREnweDQQKBQExj48trvS15j4uSsCZ51bJccMEFFBYWRkd6hMNhfvKTnzB58mQSExMB+OSTTzh8+DBFRUW8++67jB49mpSUlAZpFYI1Uuf6669n2rRpFVqjeXl5dOnShdLS0gr9ydXV3b9/f7Zv3x7tZnr11Vc577zz6hVLVa/16NGjtG/fnsTERDZs2MCXX34Z3d7r9VJaWr/7yp9xxhnRGKvSs2dPli1bBsD7779fp+NXPqYxhv3791N2P42cnBw6duxY7ykMqtJqk7zf4yYcMYQjevtD1XKICO+88w4zZ86kb9++9OvXj0AgUGFo4PDhw5kwYQIZGRlMmDCBoUOHkpqayujRoxk4cCAPPPDAScXw3nvvsWPHDu64447oBViAX/3qV4wYMYLRo0czYMCA6PbXXXcd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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "plt.clf()\n", "plt.plot(np.linspace(0, optimization_result_run.nfev, optimization_result_run.nfev), cost_run, label=\"Optimization path (run())\")\n", "upper_bound = optimization_result_run.nfev\n", "plt.plot(np.linspace(0, upper_bound, upper_bound), np.zeros(upper_bound), \"--\", label=\"Target cost\")\n", "plt.xlabel(\"Step\"); plt.ylabel(\"Cost\"); plt.legend(loc=\"upper right\"); plt.title(f\"n = {num_qubits}, l = {num_layers}, # params = {num_parameters}\")\n", "plt.grid(True)\n", "plt.show()\n", "# plt.savefig(f\"optimization_run_n_{num_qubits}_p_{num_parameters}.png\", dpi=200)" ] }, { "cell_type": "markdown", "id": "bdbc16b7", "metadata": {}, "source": [ "### Using `QJob.upgrade_parameters()`" ] }, { "cell_type": "markdown", "id": "9113393a", "metadata": {}, "source": [ "The first step now is to create the `qjob.QJob` object that which parameters we are going to upgrade in each step of the optimization; for that, we must run a circuit with initial parameters in a QPU, the procedure is as we explained above:" ] }, { "cell_type": "code", "execution_count": 10, "id": "d44dbc23", "metadata": {}, "outputs": [], "source": [ "ansatz = hardware_efficient_ansatz(num_qubits, num_layers)\n", "\n", "num_parameters = ansatz.num_parameters\n", "\n", "initial_parameters = np.zeros(num_parameters)\n", "\n", "circuit = ansatz.assign_parameters(initial_parameters)\n", "\n", "qjob = qpu.run(circuit, transpile = True, opt_level = 0, shots = n_shots)" ] }, { "cell_type": "markdown", "id": "e90da639", "metadata": {}, "source": [ "Now that we have sent to the virtual QPU the transpiled circuit, we can use the method `qjob.QJob.upgrade_parameters()` to change the rotations of the gates:" ] }, { "cell_type": "code", "execution_count": 11, "id": "ec54866c", "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Result with initial_parameters: \n", "{'000000': 100000}\n", "[4.800769084169513, 1.2169894739610545, 4.308700639089514, 0.53228562699461, 4.572801942653737, 4.101667458095368, 1.085851890521249, 1.985892017022019, 4.0998922550688555, 2.6840994154719735, 2.9905197241774086, 2.9828536502963163, 2.193155663585044, 0.5546078957132934, 4.238713832258263, 5.409654114813467, 1.6005697271722092, 0.7034015057004601, 4.371946879506468, 1.264525265909713, 4.220949121372601, 3.541658508539978, 6.122463529488255, 1.4360914785130126, 1.2467862938893313, 1.1552122896705421, 1.7255569300107159, 1.4221512402804461, 3.6973504071781655, 5.528075365251304, 0.3505244857885771, 1.8120744822423005, 0.9420785391076939, 2.6013685195652054, 5.262298544374022, 3.4664558110869947]\n", "\n", "Result with random_parameters: \n", "{'000000': 230, '000001': 1221, '000010': 57, '000011': 963, '000100': 1478, '000101': 2214, '000110': 2206, '000111': 126, '001000': 1082, '001001': 247, '001010': 265, '001011': 1762, '001100': 600, '001101': 235, '001110': 3517, '001111': 2204, '010000': 2878, '010001': 1813, '010010': 787, '010011': 2882, '010100': 689, '010101': 2055, '010110': 464, '010111': 1664, '011000': 466, '011001': 637, '011010': 194, '011011': 4234, '011100': 2381, '011101': 5051, '011110': 676, '011111': 1441, '100000': 1450, '100001': 721, '100010': 765, '100011': 3636, '100100': 1742, '100101': 5417, '100110': 609, '100111': 2073, '101000': 1626, '101001': 323, '101010': 120, '101011': 1520, '101100': 360, '101101': 4173, '101110': 1745, '101111': 517, '110000': 2991, '110001': 3656, '110010': 116, '110011': 202, '110100': 1315, '110101': 2523, '110110': 227, '110111': 904, '111000': 4230, '111001': 2280, '111010': 333, '111011': 1481, '111100': 111, '111101': 1333, '111110': 1267, '111111': 3515}\n" ] } ], "source": [ "print(\"Result with initial_parameters: \")\n", "print(qjob.result.counts)\n", "\n", "random_parameters = np.random.uniform(0, 2 * np.pi, num_parameters).tolist()\n", "print(random_parameters)\n", "qjob.upgrade_parameters(random_parameters)\n", "\n", "print()\n", "print(\"Result with random_parameters: \")\n", "print(qjob.result.counts)" ] }, { "cell_type": "markdown", "id": "ada125fd", "metadata": {}, "source": [ "**Important considerations:**\n", "\n", "- The method acepts parameters in a `list`, if you have a `numpy.array`, simply apply `.tolist()` to transform it.\n", "\n", "- When sending the circuit and setting `transpile=True`, we should be carefull that the transpilation process doesn't condense gates and combine parameters, therefore, if the user wants `cunqa`to transpile, they must set `opt_level=0`.\n", "\n", "Note that `qjob.QJob.upgrade_parameters()` is a non-blocking call, as it was `qpu.QPU.run()`." ] }, { "cell_type": "markdown", "id": "c2f430de", "metadata": {}, "source": [ "Now that we are familiar with the procedure, we can design a cost funtion that takes a set of parameters, upgrades the `qjob.QJob`, gets the result and calculates the divergence from the desired distribution:" ] }, { "cell_type": "code", "execution_count": 12, "id": "8c416b9b", "metadata": {}, "outputs": [], "source": [ "def cost_function(params):\n", " n_shots = 100000\n", " target_dist = target_distribution(num_qubits)\n", " \n", " qjob.upgrade_parameters(params.tolist())\n", " \n", " counts = qjob.result.counts\n", " \n", " return KL_divergence(counts, n_shots, target_dist)\n" ] }, { "cell_type": "markdown", "id": "a4f3a056", "metadata": {}, "source": [ "Now we are ready to start our optimization. We will use `scipy.optimize` to minimize the divergence of our result distribution from the target one:" ] }, { "cell_type": "code", "execution_count": 13, "id": "3f9016ca", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Iteration step 0: f(x) = 4.808127828547751\n", "Iteration step 10: f(x) = 3.2277447214466535\n", "Iteration step 20: f(x) = 1.4546159328320607\n", "Iteration step 30: f(x) = 0.8280370465217498\n", "Iteration step 40: f(x) = 0.7268863480351391\n", "Iteration step 50: f(x) = 0.6002777280561791\n", "Iteration step 60: f(x) = 0.6310585988580937\n", "Iteration step 70: f(x) = 0.531624604820937\n", "Iteration step 80: f(x) = 0.49413409044044915\n", "Iteration step 90: f(x) = 0.28276705189385987\n", "Iteration step 100: f(x) = 0.28315153947896776\n", "Iteration step 110: f(x) = 0.20441926998831558\n", "Iteration step 120: f(x) = 0.20818472830794493\n", "Iteration step 130: f(x) = 0.20255824135914952\n", "Iteration step 140: f(x) = 0.17140823564605984\n", "Iteration step 150: f(x) = 0.17354475356274046\n", "Iteration step 160: f(x) = 0.15458380797622073\n", "Iteration step 170: f(x) = 0.1711286646144804\n", "Iteration step 180: f(x) = 0.18543970743296512\n", "Iteration step 190: f(x) = 0.13357890094906225\n", "Iteration step 200: f(x) = 0.12809092212518816\n", "Iteration step 210: f(x) = 0.12599209681896256\n", "Iteration step 220: f(x) = 0.10140538835702381\n", "Iteration step 230: f(x) = 0.059547306164707076\n", "Iteration step 240: f(x) = 0.04761332250259291\n", "Iteration step 250: f(x) = 0.05008134672726648\n", "Iteration step 260: f(x) = 0.053307487225548414\n", "Iteration step 270: f(x) = 0.0460627777766594\n", "Iteration step 280: f(x) = 0.0412203148752551\n", "Iteration step 290: f(x) = 0.02949268274755688\n", "Iteration step 300: f(x) = 0.029702041986933683\n", "Iteration step 310: f(x) = 0.029368454456454612\n", "Iteration step 320: f(x) = 0.020242468782134097\n", "Iteration step 330: f(x) = 0.022896600154442524\n", "Iteration step 340: f(x) = 0.02440388389920066\n", "Iteration step 350: f(x) = 0.02136227945999611\n", "Iteration step 360: f(x) = 0.01699669415502308\n", "Iteration step 370: f(x) = 0.015584717555711291\n", "Iteration step 380: f(x) = 0.014365914175310331\n", "Iteration step 390: f(x) = 0.013330609474756292\n", "Iteration step 400: f(x) = 0.013018092381938575\n", "Iteration step 410: f(x) = 0.011943676000667106\n", "Iteration step 420: f(x) = 0.012111982447111237\n", "Iteration step 430: f(x) = 0.011964575772810473\n", "Iteration step 440: f(x) = 0.012022413597904028\n", "Iteration step 450: f(x) = 0.011539412823483365\n", "Iteration step 460: f(x) = 0.011866703502954939\n", "\n", " Normal return from subroutine COBYLA\n", "\n", " NFVALS = 468 F = 1.154528E-02 MAXCV = 0.000000E+00\n", " X = 1.445694E+00 4.761625E-01 1.406031E+00 1.517224E+00 -4.462020E-01\n", " 4.634130E-01 8.257513E-01 1.322178E+00 1.593436E+00 -6.847369E-02\n", " -1.637136E-01 3.511552E-01 1.616953E+00 -2.493556E-01 1.420238E+00\n", " 8.484528E-01 7.009079E-01 -1.572803E-01 1.875096E+00 -1.109267E+00\n", " 1.233791E+00 2.716658E-01 1.541790E+00 2.519028E-01 1.469977E+00\n", " 9.737024E-01 1.623521E+00 7.760460E-01 -3.942303E-01 -5.036106E-01\n", " 2.973732E-01 7.718844E-01 -3.806809E-01 3.187416E-01 5.901928E-01\n", " -8.671375E-01\n", "\n", "Total optimization time: 29.011065244674683 s\n" ] } ], "source": [ "from scipy.optimize import minimize\n", "import time\n", "\n", "i = 0\n", "\n", "initial_parameters = np.zeros(num_parameters)\n", "\n", "cost = []\n", "individuals = []\n", "\n", "def callback(xk):\n", " global i\n", " e = cost_function(xk)\n", " individuals.append(xk)\n", " cost.append(e)\n", " if i%10 == 0:\n", " print(f\"Iteration step {i}: f(x) = {e}\")\n", " i+=1\n", "\n", "tick = time.time()\n", "optimization_result = minimize(cost_function, initial_parameters, method='COBYLA',\n", " callback=callback, tol = 0.01,\n", " options={\n", " 'disp': True, # Print info during iterations\n", " 'maxiter': 4000 # Limit the number of iterations\n", " })\n", "tack = time.time()\n", "time_up = tack-tick\n", "print()\n", "print(\"Total optimization time: \", time_up, \" s\")" ] }, { "cell_type": "markdown", "id": "82f8a67e", "metadata": {}, "source": [ "We can plot the evolution of the cost function during the optimization:" ] }, { "cell_type": "code", "execution_count": 14, "id": "0f269cf9", "metadata": {}, "outputs": [ { "data": { "image/png": "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GTMm+I0gOzub8ePHs3nzZkSE4uKjsaM6v0M9evTguuuuY/To0cHfyT179tCiRenZfU877TQ2bNhQann37t0crwhswbu1BX8K6dq1K8uXLy+17siRI+zYsYOOHTsCZccHV2e8cHR0dPC9w+EILjscDrxeb5nt09PTeeyxx5g5c2bwP/+ECRN46aWXWLNmDY8++uhxT+dQUgan01luGaD8c01NTWXOnDksXryYVatW0aNHjwrLEnreFeUTGxtban+Xy4Xf7wcoc9za1H3oPO133HEHf/jDH1izZg3//Oc/q6xDYwzjx48PfqPauHEjjz32WJV5Hlu+yj670PL95S9/YejQoaSnp/PZZ5+V2q46v0NffPEFt99+OytWrKBv3754vd4y9QsEu4AqWq6tCAzw2kVzKrnwwgvJz88PfuX1+Xzce++9TJgwgbi4OAC+/fZbDh06REFBAV988QUDBw6kYcOG5OTk1EkZsrKyGDduHG+99VaplldOTg6tW7emuLi4VDdBRXl37tyZbdu2BbuW3n77bS644IIalSX0XGfNmsXAgQPJzs6mSZMmxMXFsWHDBpYuXRrc3u12l2p1Vsc555wTLCNYI1hK/sh+8MEHVZZn4MCBwWCYm5vL559/XmFe2dnZtG3bFoD//Oc/wfWDBw/mv//9LwBfffUVhw8fBqzfhw8++ID9+/cDcOjQIbZvr3h8tN/vD5b5v//9L+effz5Q8WdXWfmmT59e4XYV5b1z506GDh3KU089RXZ2Nrm5uWXqF2DTpk2lRjgdu1xbERrgtYvmVCEifPzxx7z//vt06tSJs88+m5iYmFLD/84991yuvvpqevTowciRI0lJSaFZs2YMHDiQ7t27c9999x1XGT755BO2b9/OrbfeSq9evYJdHE888QT9+vVj4MCBpS6Ijh07lmeeeYbevXuzdevW4PqYmBjefPNNxowZQ1JSEg6Hg9tuu61GZQk916uvvpqUlBRGjBiB1+vlnHPO4YEHHgh2XwBMnDgx2E1QXZdeeinz588PLk+aNIlXX32V3r17lxmqWF55+vbty8iRI+nRoweXXnopSUlJNG7cuNy8HnvsMcaMGUNycnLwoiPAo48+yvz58+nWrRsfffQRZ5xxBmB9o3vyySe5+OKL6dGjB8OHD2fPnj0Vnkt8fDxLliyhe/fuzJ07l0ceeQSo+LM71v3338+DDz5I7969K/xWVRGfz8f1119PUlISvXv35s477yQhIYHBgwezcuXKUheOFy5cyPDhwwFrgr8tW7aQkpJSo/zKVdHwmvp4nZBhkv8bb8yLtc/nZGbHYZJVefPNN83tt98eXK6L4XEnq2PPtSJ1UQejR482mzZtqnV5cnJyjDHWEMDk5GSzfPny4y5TTZTUQegw0ZPJnXfeab799ltjjDErVqww119/fTDto48+Mg8//HC5+9V0mGSEtuC1i0apykyZMqXSlnFVJk6cSK9evejTpw9XX311mVEjke7Pf/4z+fn5ABw8eJAnnngimOb1ern33nvrJB8xIV8T6ltKSooJ+zNZP7kdtsyFe+01VzSE5zmU69evr/aIipOB3aaJrY1IrIN+/fpRVFQUXPb7/cyYMYOkpKR6LFXdK+//o4gsN8aU258TocMktQWvlJ389NNPpZYj8Y9ceSK0i0Yvsiql7C8CA7yOg1dKRYYIDPDaRaOUigyRGeCNDwJ35qmTn04XXHsn03TB11xzDT///DMAF110UfDmJY/Hw+DBg2s8zlxVLQIDfOCJ5H7thz8VGGO46qqrGD16NJs3b2bTpk3k5uby0EMPVbrfsQG+TZs2Ze7CrMrjjz/ORRddVOMyHxvga3ucuhAa4KvL6/Uybdo0fvOb35SbVhtr167F5/Nx5plnAnDDDTcEP5+oqCguvPBC3nvvvVodW1UsAgN8lPVTu2lOCRVNFzxt2jTy8/OZPn06o0aNYsiQIXTq1Im///3vAKWmC77vvvtKtV6nT5/O6NGjGT58OImJibz00ktMnTqV3r17079/fw4dOgRY85V88MEHLFu2LHgHa1JSUnA+kzfeeIO+ffvSs2dPrr76avLz81m0aBGffvop9913H7169WLr1q3B44A1WVbv3r1JSkri5ptvDg7tS0xM5NFHH6VPnz4kJSWVmniqxLHnWjIzIljTBScnJ9OtWzfefPPNYB2UTBdccidryXTB3bp14+KLL6agoKDcOj92uuC7776blJQUXnjhhVLnA9aUvHB0mO4111xDly5duO6664J3a86YMYNRo0YF9xk5ciTvvvtuqfJXNmWAqp3IHCYJOpKmNr56APauqdtjtkqCS6dUmFzd6YLT09OJi4sjOTmZq666iilTppCenk5aWhpAmVkT09PTWblyJYWFhXTs2JGnnnqK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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "plt.clf()\n", "plt.plot(np.linspace(0, optimization_result.nfev, optimization_result.nfev), cost, label=\"Optimization path (upgrade_params())\")\n", "plt.plot(np.linspace(0, optimization_result_run.nfev, optimization_result_run.nfev), cost_run, label=\"Optimization path (run())\")\n", "upper_bound = max(optimization_result_run.nfev, optimization_result.nfev)\n", "plt.plot(np.linspace(0, upper_bound, upper_bound), np.zeros(upper_bound), \"--\", label=\"Target cost\")\n", "plt.xlabel(\"Step\"); plt.ylabel(\"Cost\"); plt.legend(loc=\"upper right\"); plt.title(f\"n = {num_qubits}, l = {num_layers}, # params = {num_parameters}\")\n", "plt.grid(True)\n", "plt.show()\n", "# plt.savefig(f\"optimization_n_{num_qubits}_p_{num_parameters}.png\", dpi=200)" ] }, { "cell_type": "code", "execution_count": null, "id": "fc8c4435", "metadata": {}, "outputs": [], "source": [] } ], "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.9.9" } }, "nbformat": 4, "nbformat_minor": 5 }