{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Example: VQE as QiskitPattern\n", "\n", "This tutorial will be demonstation of creating VQE as QiskitPattern as well as migration guide on how you can replicate IBM Quantum VQE custom runtime program.\n", "\n", "Let's first get information on what is VQE runtime program and what inputs and outputs it has.\n", "\n", "**Description** of runtime program is: Variational Quantum Eigensolver (VQE) to find the minimal eigenvalue of a Hamiltonian.\n", "\n", "**Inputs**:\n", "\n", "| name | type | description |\n", "| ---- | ---- | ----------- |\n", "| ansatz | object | A parameterized quantum circuit preparing the ansatz wavefunction for the VQE. It is assumed that all qubits are initially in the 0 state. | \n", "| initial_parameters|[array,string]|Initial parameters of the ansatz. Can be an array or the string ``'random'`` to choose random initial parameters.|\n", "|operator|object|The Hamiltonian whose smallest eigenvalue we're trying to find. Should be PauliSumOp|\n", "|method|str|The classical optimizer used in to update the parameters in each iteration. |\n", "\n", "\n", "**Return values**\n", "\n", "| name | type | description |\n", "| ---- | ---- | ----------- |\n", "|cost_function_evals|integer|The number of cost function (energy) evaluations.|\n", "|optimal_parameters|null|Not supported at the moment, therefore ``None``.|\n", "|optimal_point|array|The optimal parameter values found during the optimization. This is a numpy array.|\n", "|optimal_value|number|The smallest value found during the optimization. Equal to the ``eigenvalue`` attribute. This is a float.|\n", "|optimizer_evals|integer|The number of steps of the optimizer.|\n", "|optimizer_history|object|A dictionary containing information about the function evaluations (not necessarily the actual parameter value!): the current evaluation count, the parameters, the energy and the standard deviation.|\n", "|optimizer_time|number|The total time taken by the optimizer. This is a float.|\n", "\n", "We will also add optional `QiskitRuntimeService` as an argument to use that to access real devices.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With that information we can start drafting our pattern implementation in `vqe.py` file.\n", "\n", "What our pattern should do:\n", "\n", "1. parse input arguments\n", "2. create run_vqe function that accepts estimator instance, creates VQE and runs calculation\n", "3. decide which estimator to use and run vqe\n", " - if runtime service was passed then create a session and run `run_vqe` function\n", " - if runtime service was not passed then use stantard qiskit estimator\n", "4. save results from vqe\n", "\n", "Roughly our VQE pattern will look like this. Full code can be found in [vqe.py](./source_files/vqe/vqe.py) file.\n", "\n", "```python\n", "# vqe.py\n", "\n", "import ...\n", "\n", "def run_vqe(\n", " initial_parameters,\n", " ansatz,\n", " operator,\n", " estimator,\n", " method\n", "):\n", " ...\n", "\n", "arguments = get_arguments()\n", "\n", "service = arguments.get(\"service\")\n", "ansatz = arguments.get(\"ansatz\")\n", "operator = arguments.get(\"operator\")\n", "initial_parameters = arguments.get(\"initial_parameters\") \n", "optimizer = ...\n", "\n", "...\n", "\n", "if service is not None:\n", " # if we have service we need to open a session and create estimator\n", " backend = arguments.get(\"backend\", \"ibmq_qasm_simulator\")\n", " with Session(service=service, backend=backend) as session:\n", " estimator = Estimator(session=session, options=options) # qiskit_ibm_runtime.Estimator\n", " vqe_result = run_vqe( estimator=estimator, ...)\n", "else:\n", " # if we do not have a service let's use standart local estimator\n", " estimator = QiskitEstimator() # qiskit.primitives.Estimator\n", "\n", "vqe_result, callback_dict = run_vqe(\n", " initial_parameters=initial_parameters,\n", " ansatz=ansatz,\n", " operator=operator,\n", " estimator=estimator,\n", " method=method\n", ")\n", "\n", "save_result({\n", " \"optimal_point\": vqe_result.x.tolist(),\n", " \"optimal_value\": vqe_result.fun,\n", " \"optimizer_evals\": vqe_result.nfev,\n", " \"optimizer_history\": callback_dict.get(\"cost_history\", []),\n", " \"optimizer_time\": callback_dict.get(\"_total_time\", 0)\n", "})\n", "\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At this point we have our pattern implemented. Now we need to actually run it. But before let's prepare input arguments from our VQE pattern." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'ansatz': ,\n", " 'operator': SparsePauliOp(['YZ', 'ZI', 'ZZ', 'XX'],\n", " coeffs=[ 0.398 +0.j, -0.398 +0.j, -0.0113+0.j, 0.181 +0.j]),\n", " 'method': 'COBYLA',\n", " 'service': None}" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "\n", "from qiskit.circuit.library import EfficientSU2\n", "from qiskit.quantum_info import SparsePauliOp\n", "\n", "from qiskit_ibm_runtime import QiskitRuntimeService, Estimator, Session, Options\n", "\n", "USE_RUNTIME_SERVICE = False\n", "\n", "service = None\n", "if USE_RUNTIME_SERVICE:\n", " service = QiskitRuntimeService(\n", " channel='ibm_quantum',\n", " instance='ibm-q/open/main',\n", " token='',\n", " verify=False\n", " )\n", "\n", "operator = SparsePauliOp.from_list(\n", " [(\"YZ\", 0.3980), (\"ZI\", -0.3980), (\"ZZ\", -0.0113), (\"XX\", 0.1810)]\n", ")\n", "ansatz = EfficientSU2(operator.num_qubits)\n", "\n", "input_arguments = {\n", " \"ansatz\": ansatz,\n", " \"operator\": operator,\n", " \"method\": \"COBYLA\",\n", " \"service\": service,\n", "}\n", "\n", "input_arguments" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With arguments prepared we can create our qiskit serverless client, setup provider and run our pattern" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "from qiskit_serverless import ServerlessClient\n", "import os" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "serverless = ServerlessClient(\n", " token=os.environ.get(\"GATEWAY_TOKEN\", \"awesome_token\"),\n", " host=os.environ.get(\"GATEWAY_HOST\", \"http://localhost:8000\"),\n", ")\n", "serverless" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'vqe'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit_serverless import QiskitFunction\n", "\n", "if USE_RUNTIME_SERVICE:\n", " function = QiskitFunction(title=\"vqe\", entrypoint=\"vqe.py\", working_dir=\"./source_files/vqe/\")\n", "else:\n", " function = QiskitFunction(title=\"vqe\", entrypoint=\"vqe.py\", working_dir=\"./source_files/vqe/\", dependencies=[\"qiskit_aer\"])\n", "\n", "serverless.upload(function)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "job = serverless.run(\"vqe\", arguments=input_arguments)\n", "job" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'QUEUED'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "job.status()" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'result': {'0': 0.4223, '1': 0.3604, '2': 0.1073, '3': 0.11},\n", " 'optimal_point': [2.51762813907937,\n", " 1.532634671366952,\n", " 6.968201754881848,\n", " 1.8258529400009142,\n", " 1.5453234923701027,\n", " 3.905921764150066,\n", " 1.6694898480396192,\n", " 1.075020301957671,\n", " 0.8048376424004327,\n", " 2.823196594205023,\n", " 2.9665234835014846,\n", " 4.143832547893007,\n", " 4.382722375425133,\n", " 4.582108812661252,\n", " 6.596830693043498,\n", " 4.716678649450963],\n", " 'optimal_value': -0.7029303910686284,\n", " 'optimizer_time': 3.4171429999987595}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "job.result()" ] } ], "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.16" } }, "nbformat": 4, "nbformat_minor": 4 }