qiskit-documentation/docs/guides/serverless-manage-resources...

358 lines
12 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "6b7abc7b-b435-43d1-9fd8-c349ee8710f3",
"metadata": {},
"source": [
"# Manage Qiskit Serverless compute and data resources\n",
"\n",
"Qiskit Serverless allows you to manage compute and data across your [Qiskit pattern](./intro-to-patterns), including CPUs, QPUs, and other compute accelerators.\n",
"\n",
"## Parallel workflows"
]
},
{
"cell_type": "markdown",
"id": "b2d40a63-3359-46e9-8f1b-4746b449b407",
"metadata": {},
"source": [
"For classical tasks that can be parallelized, use the `@distribute_task` decorater to define compute requirements needed to perform a task. Start by recalling the `transpile_parallel.py` example from the [Write your first Qiskit Serverless program](./serverless-first-program) topic:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "475d82f0-15cc-4db3-b3b0-54b07822b2a0",
"metadata": {},
"outputs": [],
"source": [
"# /source_files/transpile_remote.py\n",
"\n",
"from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
"from qiskit_serverless import distribute_task\n",
"\n",
"@distribute_task(target={\"cpu\": 1})\n",
"def transpile_remote(circuit, optimization_level, backend):\n",
" \"\"\"Transpiles an abstract circuit into an ISA circuit for a given backend.\"\"\"\n",
" pass_manager = generate_preset_pass_manager(\n",
" optimization_level=optimization_level,\n",
"\t\tbackend=backend\n",
" )\n",
" isa_circuit = pass_manager.run(circuit)\n",
" return isa_circuit"
]
},
{
"cell_type": "markdown",
"id": "a5914f1d-f898-4db4-8d1e-ccc8081883b9",
"metadata": {},
"source": [
"In this example, you decorated the `transpile_remote()` method with `@distribute_task(target={\"cpu\": 1})`. When run, this creates an asynchronous parallel worker task with a single CPU core, and returns with a reference to track the worker. To fetch the result, pass the reference to the `get()` method:"
]
},
{
"cell_type": "markdown",
"id": "74fdcd4a-01cd-46ca-aa24-2a8a3605346f",
"metadata": {},
"source": [
"```python\n",
"from qiskit_serverless import get\n",
"\n",
"transpile_worker_reference = transpile_remote(circuit, optimization_level, backend)\n",
"result = get(transpile_worker_reference)\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "268d5ef6-12cf-4ba5-95a3-f7460e4b3bfc",
"metadata": {},
"source": [
"You can also create and run multiple parallel tasks as follows:"
]
},
{
"cell_type": "markdown",
"id": "ac99b4a0-4a42-4c43-869d-265344b70359",
"metadata": {},
"source": [
"```python\n",
"transpile_worker_references = [\n",
" transpile_remote(circuit, optimization_level, backend)\n",
" for circuit in circuit_list\n",
"]\n",
"\n",
"results = get(transpile_worker_references)\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "611fe030-4494-46b5-9ea1-9678ac513210",
"metadata": {},
"source": [
"### Explore different task configurations\n",
"\n",
"You can flexibly allocate CPU, GPU, and memory for your tasks via `@distribute_task()`. For Qiskit Serverless on IBM Quantum™ Platform, each program is equipped with 16 CPU cores and 32 GB RAM, which can be allocated dynamically as needed.\n",
"\n",
"CPU cores can be allocated as full CPU cores, or even fractional allocations, as shown in the following.\n",
"\n",
"Memory is allocated in number of bytes. Recall that there are 1024 bytes in a kilobyte, 1024 kilobytes in a megabyte, and 1024 megabytes in a gigabyte. To allocate 2 GB of memory for your worker, you need to allocate `\"mem\": 2 * 1024 * 1024 * 1024`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cea90969-cfbf-4181-9ffa-524f3709dc69",
"metadata": {},
"outputs": [],
"source": [
"@distribute_task(target={\n",
" \"cpu\": 16,\n",
" \"mem\": 32 * 1024 * 1024 * 1024\n",
"})\n",
"def transpile_remote(circuit, optimization_level, backend):\n",
" return None"
]
},
{
"cell_type": "markdown",
"id": "f6fff174-301f-47ca-b7c6-69bb5298fd14",
"metadata": {},
"source": [
"## Automatic QPU selection\n",
"\n",
"This example demonstrates how to use `IBMQPUSelector` to automate the process of selecting which qubits to use from a set of available QPUs. Instead of manually selecting a QPU, Qiskit Serverless automatically allocates a QPU according to desired criteria. `IBMQPUSelector`s are imported from server-side `qiskit_serverless_tools.selectors` modules, and must be invoked from your uploaded program.\n",
"\n",
"Here, `IBMLeastNoisyQPUSelector` finds the QPU that yields the least-noisy qubit subgraph for the input circuit, from among the QPUs available to you through your IBM Quantum account.\n",
"\n",
"For each `IBMQPUSelector`, the context is set in the constructor. All `IBMQPUSelectors` require Qiskit Runtime credentials. The `IBMLeastNoisyQPUSelector` requires a circuit and transpile options specifying how the circuit should be optimized for each QPU, to determine the most optimal QPU and qubit layout.\n",
"\n",
"All `IBMQPUSelectors` implement a `get_backend` method, which retrieves the optimal QPU with respect to the given context. The `get_backend` method also allows for additional filtering of the QPUs. It is implemented using the same interface as the [QiskitRuntimeService.backends](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.QiskitRuntimeService#backends) method."
]
},
{
"cell_type": "markdown",
"id": "b9b38d02-1c4b-4598-abc8-f2ff2f3c1d23",
"metadata": {},
"source": [
"```python\n",
"# source_files/transpile_parallel.py\n",
"\n",
"from qiskit_ibm_runtime import QiskitRuntimeService\n",
"from qiskit.circuit.random import random_circuit\n",
"from qiskit_serverless_tools.selectors import IBMLeastNoisyQPUSelector\n",
"\n",
"service = QiskitRuntimeService(channel='ibm_quantum', token=API_TOKEN)\n",
"\n",
"abstract_circuit = random_circuit(\n",
" num_qubits=100, depth=4, measure=True\n",
")\n",
"\n",
"selector = IBMLeastNoisyQPUSelector(\n",
" service, circuit=abstract_circuit, transpile_options={\"optimization_level\": 3}\n",
")\n",
"backend = selector.get_backend(min_num_qubits=127)\n",
"target_circuit = selector.optimized_circuit\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "8eda9b3e-31c3-4783-b5f6-a72bd31e41b6",
"metadata": {},
"source": [
"You can also use the `IBMLeastBusyQPUSelector` to find a QPU that can support the circuit width but with the shortest queue."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10bdb211-0d53-4269-aa92-51ecf494bd63",
"metadata": {},
"outputs": [],
"source": [
"# source_files/transpile_parallel.py\n",
"\n",
"from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
"from qiskit_serverless_tools.selectors import IBMLeastBusyQPUSelector\n",
"\n",
"backend = IBMLeastBusyQPUSelector(service).get_backend(min_num_qubits=127)\n",
"pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
"target_circuit = pm.run(abstract_circuit)"
]
},
{
"cell_type": "markdown",
"id": "6bc45489-56d0-4f46-8659-9df4d1555516",
"metadata": {},
"source": [
"## Manage data across your program\n",
"\n",
"Qiskit Serverless allows you to manage files in the `/data` directory across all your programs. This includes several limitations:\n",
"\n",
"- Only `tar` and `h5` files are supported today\n",
"- This is only a flat `/data` storage, and cannot have `/data/folder/` subdirectories\n",
"\n",
"The following shows how to upload files. Be sure you have authenticated to Qiskit Serverless with your [IBM Quantum account](https://quantum.ibm.com/account) (see [Deploy to IBM Quantum Platform](./serverless-first-program#deploy-to-ibm-quantum-platform) for instructions)."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "0183278f-8ce3-4466-9255-097b2d211052",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"message\":\"/usr/src/app/media/5f37582aa306c50013fac285/transpile_demo.tar\"}'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import tarfile\n",
"\n",
"# Create a tar\n",
"filename = \"transpile_demo.tar\"\n",
"file = tarfile.open(filename,\"w\")\n",
"file.add(\"source_files/transpile_remote.py\")\n",
"file.close()\n",
"\n",
"# Upload the tar to Serverless data directory\n",
"serverless.file_upload(filename)"
]
},
{
"cell_type": "markdown",
"id": "4f762470-945f-48d5-a65b-c60d3b2dae3f",
"metadata": {},
"source": [
"Next, you can list all the files in your `data` directory. This data is accessible to all programs."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "14241fc4-d0cb-4803-8752-a460e1f48708",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['transpile_demo.tar']"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"serverless.files()"
]
},
{
"cell_type": "markdown",
"id": "a97bd83e-8250-43bb-b1c4-d40d822c7ba2",
"metadata": {},
"source": [
"This can be done from a program by using `file_download()` to download the file to the program environment, and uncompressing the `tar`."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "ef649b2a-ed95-4dd2-89d9-61438faa7c1e",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 10.2k/10.2k [00:00<00:00, 10.0MiB/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"downloaded_3d3c4bf6_transpile_demo.tar\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"# transpile_parallel.py\n",
"\n",
"import tarfile\n",
"\n",
"files = serverless.files()\n",
"demo_file = files[0]\n",
"downloaded_tar = serverless.file_download(demo_file)\n",
"\n",
"print(downloaded_tar)\n",
"with tarfile.open(downloaded_tar, 'r') as tar:\n",
" tar.extractall()"
]
},
{
"cell_type": "markdown",
"id": "5b93dbdb-2060-468b-8496-ba98142a780b",
"metadata": {},
"source": [
"At this point, your program can interact with the files, as you would a local experiment. `file_upload()` , `file_download()`, and `file_delete()` can be called from your local experiment, or your uploaded program, for consistent and flexible data management."
]
},
{
"cell_type": "markdown",
"id": "a004dd78-0e0a-4a3b-83cb-333469533ef6",
"metadata": {},
"source": [
"## Next steps\n",
"\n",
"<Admonition type=\"info\" title=\"Recommendations\">\n",
"\n",
"- See a full example that [ports existing code to Qiskit Serverless ](./serverless-port-code).\n",
"- Read a paper in which researchers used Qiskit Serverless and quantum-centric supercomputing to [explore quantum chemistry](https://arxiv.org/abs/2405.05068v1).\n",
"\n",
"</Admonition>"
]
}
],
"metadata": {
"description": "Manage compute and data across your Qiskit pattern with Qiskit Serverless.",
"kernelspec": {
"display_name": "Python 3",
"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"
},
"title": "Manage Qiskit Serverless compute and data resources"
},
"nbformat": 4,
"nbformat_minor": 2
}