{ "cells": [ { "cell_type": "markdown", "id": "26c21ec5", "metadata": {}, "source": [ "# Example for execution of multiple circuits in QPUs" ] }, { "cell_type": "markdown", "id": "df8dc942", "metadata": {}, "source": [ "Before executing, you must set up and `qraise` the QPUs, check the `README.md` for instructions. For this examples it will be optimal to have more than one QPU and at least one of them with ideal AerSimulator." ] }, { "cell_type": "markdown", "id": "86cdbafe", "metadata": {}, "source": [ "### Importing and adding paths to `sys.path`" ] }, { "cell_type": "code", "execution_count": 1, "id": "c532632a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/mnt/netapp1/Store_CESGA/home/cesga/jvazquez/works/cunqa/installation\n", "/mnt/netapp1/Store_CESGA//home/cesga/jvazquez/.api_simulator/qpus.json\n", "/opt/cesga/qmio/hpc/software/Compiler/gcc/12.3.0/boost/1.85.0/lib:/opt/cesga/qmio/hpc/software/Compiler/gcc/12.3.0/flexiblas/3.3.0/lib:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/ompi/lib:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/nccl_rdma_sharp_plugin/lib:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/sharp/lib:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/hcoll/lib:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/ucc/lib/ucc:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/ucc/lib:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/ucx/lib/ucx:/mnt/netapp1/Optcesga_FT2_RHEL7/qmio/hpc/software/Core/hpcx/2.17.1/ucx/lib:/opt/cesga/qmio/hpc/software/Compiler/gcc/12.3.0/openblas/0.3.24/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/libjpeg-turbo/3.0.2/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/libgd/2.3.3/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/spdlog/1.9.2/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/symengine/0.11.2/lib64:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/flint/3.1.2/lib64:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/mpc/1.3.1/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/mpfr/4.2.1/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/gmp/6.3.0/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/llvm/16.0.0/lib:/opt/cesga/qmio/hpc/software/Core/imkl/2023.2.0/mkl/2023.2.0/lib/intel64:/opt/cesga/qmio/hpc/software/Core/imkl/2023.2.0/compiler/2023.2.0/linux/compiler/lib/intel64_lin:/opt/cesga/qmio/hpc/software/Core/rust/1.75.0/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/python/3.9.9/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/libffi/3.4.2/lib64:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/sqlite/3.45.3/lib:/opt/cesga/qmio/hpc/software/Compiler/gcccore/12.3.0/binutils/2.40/lib:/opt/cesga/qmio/hpc/software/Core/gcccore/12.3.0/lib64\n" ] } ], "source": [ "import os, sys\n", "\n", "# path to access c++ files\n", "installation_path = os.getenv(\"INSTALL_PATH\")\n", "sys.path.append(installation_path)\n", "\n", "print(installation_path)\n", "print(os.getenv(\"INFO_PATH\"))\n", "\n", "print(os.environ.get('LD_LIBRARY_PATH'))\n" ] }, { "cell_type": "markdown", "id": "368e94bf", "metadata": {}, "source": [ "### Let's get the QPUs that we q-raised!" ] }, { "cell_type": "code", "execution_count": 2, "id": "c2d0c54e", "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "QPU 0, backend: BasicAer, simulator: AerSimulator, version: 0.0.1.\n", "QPU 1, backend: BasicAer, simulator: AerSimulator, version: 0.0.1.\n" ] } ], "source": [ "from cunqa import getQPUs\n", "\n", "qpus = getQPUs()\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": "0b312b18", "metadata": {}, "source": [ "The method `getQPUs()` accesses the information of the raised QPus and instanciates one `qpu.QPU` object for each, returning a list. If you are working with `jupyter notebook` we recomend to instanciate this method just once.\n", "\n", "About the `qpu.QPU` objects:\n", "\n", "- `QPU.id`: identificator of the virtual QPU, they will be asigned from 0 to n-1.\n", "\n", "\n", "- `QPU.backend`: object `backend.Backend` that has information about the simulator and backend for the given QPU.\n" ] }, { "cell_type": "markdown", "id": "2ce62634", "metadata": {}, "source": [ "### Let's create a circuit to run in our QPUs!" ] }, { "cell_type": "markdown", "id": "29555d4b", "metadata": {}, "source": [ "We can create the circuit using `qiskit` or writting the instructions in the `json` format specific for `cunqa` (check the `README.md`), `OpenQASM2` is also supported. Here we choose not to complicate things and we create a `qiskit.QuantumCircuit`:" ] }, { "cell_type": "code", "execution_count": 3, "id": "c5350387", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
┌───┐┌──────┐┌───────┐ ░ ┌─┐ \n", " q_0: ┤ X ├┤0 ├┤0 ├─░─┤M├────────────\n", " └───┘│ ││ │ ░ └╥┘┌─┐ \n", " q_1: ─────┤1 ├┤1 ├─░──╫─┤M├─────────\n", " │ ││ │ ░ ║ └╥┘┌─┐ \n", " q_2: ─────┤2 QFT ├┤2 IQFT ├─░──╫──╫─┤M├──────\n", " ┌───┐│ ││ │ ░ ║ ║ └╥┘┌─┐ \n", " q_3: ┤ X ├┤3 ├┤3 ├─░──╫──╫──╫─┤M├───\n", " ├───┤│ ││ │ ░ ║ ║ ║ └╥┘┌─┐\n", " q_4: ┤ X ├┤4 ├┤4 ├─░──╫──╫──╫──╫─┤M├\n", " └───┘└──────┘└───────┘ ░ ║ ║ ║ ║ └╥┘\n", "meas: 5/══════════════════════════╩══╩══╩══╩══╩═\n", " 0 1 2 3 4" ], "text/plain": [ " ┌───┐┌──────┐┌───────┐ ░ ┌─┐ \n", " q_0: ┤ X ├┤0 ├┤0 ├─░─┤M├────────────\n", " └───┘│ ││ │ ░ └╥┘┌─┐ \n", " q_1: ─────┤1 ├┤1 ├─░──╫─┤M├─────────\n", " │ ││ │ ░ ║ └╥┘┌─┐ \n", " q_2: ─────┤2 QFT ├┤2 IQFT ├─░──╫──╫─┤M├──────\n", " ┌───┐│ ││ │ ░ ║ ║ └╥┘┌─┐ \n", " q_3: ┤ X ├┤3 ├┤3 ├─░──╫──╫──╫─┤M├───\n", " ├───┤│ ││ │ ░ ║ ║ ║ └╥┘┌─┐\n", " q_4: ┤ X ├┤4 ├┤4 ├─░──╫──╫──╫──╫─┤M├\n", " └───┘└──────┘└───────┘ ░ ║ ║ ║ ║ └╥┘\n", "meas: 5/══════════════════════════╩══╩══╩══╩══╩═\n", " 0 1 2 3 4 " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from qiskit import QuantumCircuit\n", "from qiskit.circuit.library import QFT\n", "\n", "n = 5 # number of qubits\n", "\n", "qc = QuantumCircuit(n)\n", "\n", "qc.x(0); qc.x(n-1); qc.x(n-2)\n", "\n", "qc.append(QFT(n), range(n))\n", "\n", "qc.append(QFT(n).inverse(), range(n))\n", "\n", "qc.measure_all()\n", "\n", "display(qc.draw())" ] }, { "cell_type": "markdown", "id": "bf2f0f88", "metadata": {}, "source": [ "### Execution time! Let's do it sequentially" ] }, { "cell_type": "code", "execution_count": 4, "id": "c5c6682c", "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "For QPU 0, with backend BasicAer:\n", "Result: \n", "{'11001': 1000}\n", " Time taken: 0.019545911 s.\n", "For QPU 1, with backend BasicAer:\n", "Result: \n", "{'11001': 1000}\n", " Time taken: 0.000873561 s.\n" ] } ], "source": [ "counts = []\n", "\n", "for i, qpu in enumerate(qpus):\n", "\n", " print(f\"For QPU {qpu.id}, with backend {qpu.backend.name}:\")\n", " \n", " # 1)\n", " qjob = qpu.run(qc, transpile = True, shots = 1000)# non-blocking call\n", "\n", " # 2)\n", " result = qjob.result() # bloking call\n", "\n", " # 3)\n", " time = qjob.time_taken()\n", " counts.append(result.get_counts())\n", "\n", " print(f\"Result: \\n{result.get_counts()}\\n Time taken: {time} s.\")" ] }, { "cell_type": "markdown", "id": "a0ed6a3b", "metadata": {}, "source": [ "1. First we run the circuit with the method `QPU.run()`, passing the circuit, transpilation options and other run parameters. It is important to note that if we don´t specify `transpilation=True`, default is `False`, therefore the user will be responsible for the tranpilation of the circuit accordingly to the native gates and topology of the backend. This method will return a `qjob.QJob` object. Be aware that the key point is that the `QPU.run()` method is **asynchronous**.\n", "\n", "\n", "2. To get the results of the simulation, we apply the method `QJob.result()`, which will return a `qjob.Result` object that stores the information in its class atributes. Depending on the simulator, we will have more or less information. Note that this is a **synchronous** method.\n", "\n", "\n", "3. Once we have the `qjob.Result` object, we can obtain the counts dictionary by `Result.get_counts()`. Another method independent from the simulator is `Result.time_taken()`, that gives us the time of the simulation in seconds." ] }, { "cell_type": "code", "execution_count": 5, "id": "94a4e3b4", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAArYAAAFGCAYAAACfedxoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAbzElEQVR4nO3df7BfdX3n8df75mJUCAiBRAIJMRh+JRAqd6hbMy3K0qJWhLXTVWYKI6XpFNnGSrvFrTOltdiy1u0W6MhahdplV0trpYLuumjJpEyxNYlGgsUKCgk/En4nKFC83s/+cb9pLzE/7g335pucPB4zmdz7Oef7/b5vxnGec/jcc6q1FgAA2NcN9HsAAACYDMIWAIBOELYAAHSCsAUAoBOELQAAnSBsAQDohMF+D7AnHH744W3+/Pn9HgMAYJdWr179eGvtiO2szxocHPx4ksXZfy9OjiRZNzw8fPFpp5326LYH94uwnT9/flatWtXvMQAAdqmqHtje+uDg4Mdf/epXn3jEEUc8NTAwsF8+iGBkZKQee+yxkzZu3PjxJOdse3x/rX0AgH3N4iOOOGLL/hq1STIwMNCOOOKIzRm9av2jx/fwPAAA7J6B/Tlqt+r9G2y3YYUtAADjct999x1w5plnHnvMMccsPvroo0++4IIL5j333HOVJLfeeuuMGTNmnHrCCSectGDBgkWXXXbZkUly9dVXz7zgggvmjX2f008//fiVK1e+ctv3v+eee152yimnnDBv3rzFb33rWxc8//zzNZH59os9tgAAXTP/8s+fNpnvd/8fvHX1zo6PjIzk3HPPfe3FF1/86PLly+8bHh7O+eeff8wll1xy9A033LAhSYaGhr53++2337tly5aBk08++aTzzjtv80RmeN/73nf0pZdeumnZsmVPnX/++fP++I//+PDf/M3ffGy8r3fFFgCAXbrllltmTJ8+fWT58uVPJMng4GCuu+66DZ/5zGdmbt68+UVNefDBB4+cfPLJz95zzz3Tx/v+IyMjufPOO2e8+93vfipJLrrooiduueWWV01kRmELAMAu3XXXXa9YsmTJs2PXDjvssJGjjjrqhbvvvvtFAbtx48ZpX/va1w489dRTnxvv+2/atGlwxowZPzzggAOSJPPnz39h06ZNL5vIjLYiAAAwKVatWnXQiSeeeNLAwEBbvnz5xqGhoefvvPPOA7d3btWEts+Oi7AFAGCXFi9e/NzNN9986Ni1J598cuDxxx8fPOWUU55fsWLFQVv32I495/DDDx9++umnp41de/rpp6fNnj17eOza7Nmzh5955plpP/jBD3LAAQfk/vvvf9ns2bNfmMiMtiIAALBL55xzzjPPP//8wLXXXjszSYaHh3PJJZfMveiiix496KCDdngbsqVLl35/9erVB61fv34wSVauXPnKF154YeDYY499UbQODAzk9a9//TM33HDDoUly/fXXz/zZn/3Zpycyo7AFAGCXBgYGcvPNN9/713/914cec8wxiw899NBTBwYGctVVV23c2evmzp07fNVVV204++yzF55wwgkn/dqv/drcG2+88TvTpk37kXM/8pGPPHjNNde8et68eYufeuqpweXLlz8+kRmrte7f53doaKh5pC4AsC+oqtWttaFt19euXXv/kiVLJhR6U+m222478MILL1xw00033bd06dJnd/2KybN27drDlyxZMn/bdXtsAQCYsLPOOuv7Dz/88F39nmMsWxEAAOgEYQsAQCcIWwAAOkHYAgDQCcIWAIBOELYAAIzLfffdd8CZZ5557DHHHLP46KOPPvmCCy6Y99xzz1WS3HrrrTNmzJhx6gknnHDSggULFl122WVHJsnVV18984ILLpg39n1OP/3041euXPnKbd//Qx/60BHz5s1bXFWnPfLIIxO+e5fbfQEA7IuuOOS0yX2/zat3dnhkZCTnnnvuay+++OJHly9fft/w8HDOP//8Yy655JKjb7jhhg1JsvWRulu2bBk4+eSTTzrvvPM2T2SEn/qpn/reO97xjs1vetObjt+dH2GPXbGtquur6tGqWjdm7bCquq2qvt37+9DeelXV1VV1b1V9o6peN+Y1F/bO/3ZVXbin5gcA2J/dcsstM6ZPnz6yfPnyJ5JkcHAw11133YbPfOYzMzdv3vyipjz44INHTj755Gfvueee6RP5jDe84Q3PHX/88S/s+szt25NbEf4sydnbrF2e5MuttYVJvtz7PknenGRh78+yJB9NRkM4yW8n+fEkpyf57a0xDADA1LnrrrtesWTJkhc9Yeywww4bOeqoo164++67XxSwGzdunPa1r33twFNPPfW5PTnjHgvb1trKJE9us/z2JJ/sff3JJOeOWf/zNuorSV5VVUcm+Zkkt7XWnmytPZXktvxo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\n", "text/plain": [ "