{ "cells": [ { "cell_type": "markdown", "id": "f6fb114d-af27-4a89-83b4-b3806dfa8c1a", "metadata": {}, "source": [ "# Running QiskitPattern using decorators (Experimental)\n", "\n", "In this tutorial we will describe alternative way (interface) of running your patterns.\n", "\n", "This new interface provides a way to define pattern as python function and run it in a single file, using `@distribute_qiskit_function` decorator.\n", "All you need to do is annotate function with `@distribute_qiskit_function` decorator and call it. \n", "As a result of the call of the function you will get `Job` handle to check it's progress like we did in previous tutorials.\n", "\n", "Limitations of this interface:\n", "\n", "- Functions decorated with distribute_qiskit_function, can only accept named arguments for now. E.g do not use `my_pattern(argument1)`, instead specify name of the argument `my_pattern(argument1=argument1)`\n", "- Function return will run `qiskit_serverless.save_result` function under the hood, which means return values must be json serializable values in form of dictionary (with values as all Python native types, like strings, lists, dicts, `numpy` arrays, `QuantumCircuit`, `Operator`, etc.)\n", "- When using local folder/modules user must specify `working_dir` as `./` (current folder), which will be archiving and sending content of entire folder for remote execution. Make sure that folder does not have large files. \n", "\n", "> ⚠ This interface is experimental, therefore it is subjected to breaking changes.\n", "\n", "> ⚠ This provider is set up with default credentials to a test cluster intended to run on your machine. For information on setting up infrastructure on your local machine, check out the guide on [local infrastructure setup](https://qiskit.github.io/qiskit-serverless/deployment/local.html)." ] }, { "cell_type": "code", "execution_count": 1, "id": "2bf315de-a4fe-429b-9fdd-7a2f2efe2278", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "from qiskit_serverless import ServerlessClient\n", "\n", "provider = ServerlessClient(\n", " token=os.environ.get(\"GATEWAY_TOKEN\", \"awesome_token\"),\n", " host=os.environ.get(\"GATEWAY_HOST\", \"http://localhost:8000\"),\n", ")\n", "provider" ] }, { "cell_type": "markdown", "id": "e26dc3bc-6ca7-4130-91c4-162c914288a8", "metadata": {}, "source": [ "## Hello, Qiskit!\n", "\n", "Let's create simpliest pattern by writing a funtion `hello_qiskit` and annotating it with `@distribute_qiskit_function` decorator. \n", "The ``distribute_qiskit_function`` decorator accepts a [BaseProvider](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.core.BaseProvider.html) instance for the ``provider`` argument. Other arguments are `dependencies` to specify extra packages to install during execution and `working_dir` to specify working directory that will be shiped for remote execution if needed." ] }, { "cell_type": "code", "execution_count": 2, "id": "05e759d5-4e20-407f-ac67-df1ab02a6849", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit import QuantumCircuit\n", "from qiskit.primitives import StatevectorSampler as Sampler\n", "\n", "from qiskit_serverless import distribute_qiskit_function, distribute_task, get\n", "\n", "\n", "@distribute_qiskit_function(provider)\n", "def hello_qiskit():\n", " circuit = QuantumCircuit(2)\n", " circuit.h(0)\n", " circuit.cx(0, 1)\n", " circuit.measure_all()\n", " circuit.draw()\n", "\n", " sampler = Sampler()\n", " quasi_dists = sampler.run([(circuit)]).result()[0].data.meas.get_counts()\n", "\n", " return quasi_dists\n", "\n", "\n", "job = hello_qiskit()\n", "job" ] }, { "cell_type": "code", "execution_count": 3, "id": "ec3fc6cc-25c4-4830-b5c3-f73b9e9eceb6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'0': 0.4999999999999999, '3': 0.4999999999999999}]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "job.result()" ] }, { "cell_type": "markdown", "id": "d5192fa3-70a7-4b22-b2de-04d4b1816323", "metadata": {}, "source": [ "## QiskitPattern with distributed tasks\n", "\n", "As in previous examples you can define distributed tasks and call them within a pattern." ] }, { "cell_type": "code", "execution_count": 4, "id": "a1044e82-3cb9-46f0-9b5b-05a3410f5507", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit_serverless import get_arguments, save_result, distribute_task, get\n", "from qiskit import QuantumCircuit\n", "from qiskit.primitives import StatevectorSampler as Sampler\n", "from qiskit.circuit.random import random_circuit\n", "\n", "\n", "@distribute_task(target={\"cpu\": 2})\n", "def distributed_sample(circuit: QuantumCircuit):\n", " \"\"\"Distributed task that returns quasi distribution for given circuit.\"\"\"\n", " return Sampler().run([(circuit)]).result()[0].data.meas.get_counts()\n", "\n", "\n", "@distribute_qiskit_function(provider)\n", "def pattern_with_distributed_tasks(circuits):\n", " sample_task_references = [distributed_sample(circuit) for circuit in circuits]\n", " results = get(sample_task_references)\n", " print(results)\n", "\n", "\n", "circuits = []\n", "for _ in range(3):\n", " circuit = random_circuit(2, 2)\n", " circuit.measure_all()\n", " circuits.append(circuit)\n", "\n", "job = pattern_with_distributed_tasks(circuits=circuits)\n", "job" ] }, { "cell_type": "code", "execution_count": 5, "id": "03c7bdbb-fb4d-461f-9235-ce970064de1a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OpenBLAS WARNING - could not determine the L2 cache size on this system, assuming 256k\n", "OpenBLAS WARNING - could not determine the L2 cache size on this system, assuming 256k\n", "2023-11-29 07:44:50,932\tINFO worker.py:1354 -- Using address 172.18.0.2:6379 set in the environment variable RAY_ADDRESS\n", "2023-11-29 07:44:50,933\tINFO worker.py:1489 -- Connecting to existing Ray cluster at address: 172.18.0.2:6379...\n", "2023-11-29 07:44:50,951\tINFO worker.py:1664 -- Connected to Ray cluster. View the dashboard at \u001b[1m\u001b[32m172.18.0.2:8265 \u001b[39m\u001b[22m\n", "\u001b[36m(pid=1191)\u001b[0m OpenBLAS WARNING - could not determine the L2 cache size on this system, assuming 256k\n", "[[{0: 1.0}], [{0: 0.5, 1: 0.5}], [{2: 1.0}]]\n", "\u001b[36m(pid=1193)\u001b[0m OpenBLAS WARNING - could not determine the L2 cache size on this system, assuming 256k\u001b[32m [repeated 5x across cluster] (Ray deduplicates logs by default. Set RAY_DEDUP_LOGS=0 to disable log deduplication, or see https://docs.ray.io/en/master/ray-observability/ray-logging.html#log-deduplication for more options.)\u001b[0m\n", "\n" ] } ], "source": [ "job.result()\n", "print(job.logs())" ] }, { "cell_type": "markdown", "id": "ca90f9be-aac9-4ed1-8384-463698914994", "metadata": {}, "source": [ "## QiskitPattern with local modules/folders\n", "\n", "Situation with local folders is a little bit trickier. In order to make local imports work in remote execution of a pattern we need to specify `working_dir` argument for `distribute_qiskit_function` decorator. It will tell qiskit serverless to ship all content of current folder to remote cluster, which will make local folders discoverable by Python interpreter during remote execution.\n", "\n", "In this example we will use local folder `source_files` with `circuit_utils.py` file, which has implementation of `create_hello_world_circuit` function. " ] }, { "cell_type": "code", "execution_count": 6, "id": "8acd98bc-3443-447a-b8a1-c5e34a292f3b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "from qiskit_serverless import distribute_qiskit_function, distribute_task, get, save_result\n", "from qiskit.primitives import StatevectorSampler as Sampler\n", "\n", "from source_files.circuit_utils import create_hello_world_circuit\n", "\n", "\n", "@distribute_qiskit_function(provider, working_dir=\"./\")\n", "def my_function_with_modules():\n", " quasi_dists = Sampler().run([(create_hello_world_circuit())]).result()[0].data.meas.get_counts()\n", " return {\"quasi_dists\": quasi_dists}\n", "\n", "\n", "job = my_function_with_modules()\n", "job" ] }, { "cell_type": "code", "execution_count": 7, "id": "258c7495-3c4f-4dbd-8fe0-1bc46f881cbe", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'quasi_dists': [{'0': 0.4999999999999999, '3': 0.4999999999999999}]}" ] }, "execution_count": 7, "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": 5 }