{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Parallel workflows in QiskitPatterns\n", "\n", "In this document, we will learn how to run distributed workflows inside a pattern. In this case, we will compute the quasi-probability distribution in parallel for a list of quantum circuits.\n", "\n", "Let's take a look at the pattern file [./source_files/pattern_with_parallel_workflow.py](./source_files/pattern_with_parallel_workflow.py). \n", "\n", "```python\n", "# source_files/pattern_with_parallel_workflow.py\n", "\n", "from qiskit_serverless import get_arguments, save_result, distribute_task, get\n", "\n", "from qiskit import QuantumCircuit\n", "from qiskit.primitives import Sampler\n", "\n", "\n", "@distribute_task()\n", "def distributed_sample(circuit: QuantumCircuit):\n", " \"\"\"Distributed task that returns quasi distribution for given circuit.\"\"\"\n", " return Sampler().run(circuit).result().quasi_dists[0]\n", "\n", "\n", "arguments = get_arguments()\n", "circuits = arguments.get(\"circuits\")\n", "\n", "\n", "# run distributed tasks as async function\n", "# we get task references as a return type\n", "sample_task_references = [\n", " distributed_sample(circuit)\n", " for circuit in circuits\n", "]\n", "\n", "# now we need to collect results from task references\n", "results = get(sample_task_references)\n", "\n", "save_result({\n", " \"results\": results\n", "})\n", "```\n", "\n", "There are a lot of new concepts introduced in this pattern, so let's go over them in more detail:\n", "\n", "The [distribute_task](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.core.distribute_task.html#qiskit_serverless.core.distribute_task) decorator converts a function into a distributed task. This means that the function will be executed on compute resources asynchronously and in parallel to the main context of the pattern.\n", "\n", "When you call a converted function, it will return a reference to the function execution instead of the result. In order to get the result back, you need to call the [get](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.core.get.html#qiskit_serverless.core.get) function on the function reference. `get` will wait until the function is finished and return the result of the function execution.\n", "\n", "In the pattern above, we have applied the `distribute_task` decorator to the `distributed_sample` function. This function takes a `QuantumCircuit` as input and returns the quasi distribution for that circuit.\n", "\n", "After we have defined the `distributed_sample` function, we read the circuits from the pattern arguments using the [get_arguments](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.serializers.get_arguments.html#qiskit_serverless.serializers.get_arguments) function. We then call the `distributed_sample` function for each of the circuits, which creates a reference to each of the function executions.\n", "\n", "These function executions will run in parallel on compute resources, and we get task references as the return type. We store these task references in the `sample_task_references` list.\n", "\n", "After we have created the task references for each of the function executions, we need to collect the results from these tasks. We do this by calling the `get` function on the list of task references, which waits until all the tasks have completed and returns the results.\n", "\n", "Once we have the results, we can save them using the [save_result](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.core.save_result.html#qiskit_serverless.core.save_result) function.\n", "\n", "Essentially, this pattern reads the circuits from the pattern arguments, executes the `distributed_sample` function on each circuit in parallel, collects the results from the function executions, and saves the results." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> ⚠ 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, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit_serverless import ServerlessClient\n", "import os\n", "\n", "client = ServerlessClient(\n", " token=os.environ.get(\"GATEWAY_TOKEN\", \"awesome_token\"),\n", " host=os.environ.get(\"GATEWAY_HOST\", \"http://localhost:8000\"),\n", ")\n", "\n", "client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a list of random circuits which we will be passed as arguments to the pattern." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[,\n", " ,\n", " ]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit.circuit.random import random_circuit\n", "\n", "circuits = [random_circuit(2, 2) for _ in range(3)]\n", "[circuit.measure_all() for circuit in circuits]\n", "circuits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Run pattern as usual, but pass the circuits in as a keyword argument, `circuits`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'pattern-with-parallel-workflow'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit_serverless import QiskitFunction\n", "\n", "function = QiskitFunction(\n", " title=\"pattern-with-parallel-workflow\",\n", " entrypoint=\"pattern_with_parallel_workflow.py\",\n", " working_dir=\"./source_files/\",\n", ")\n", "\n", "client.upload(function)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "parallel_function = client.get(\"pattern-with-parallel-workflow\")\n", "job = parallel_function.run(circuits=circuits)\n", "job" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'QUEUED'" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "job.status()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'results': [{'0': 0.5474338956985804, '3': 0.4525661043014198},\n", " {'0': 0.9999999999999998},\n", " {'0': 0.9572706116932064, '3': 0.0427293883067935}]}" ] }, "execution_count": 6, "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.19" } }, "nbformat": 4, "nbformat_minor": 4 }