diff --git a/docs/guides/serverless-manage-resources.ipynb b/docs/guides/serverless-manage-resources.ipynb index 33813a01eb4..d00a6870c69 100644 --- a/docs/guides/serverless-manage-resources.ipynb +++ b/docs/guides/serverless-manage-resources.ipynb @@ -16,7 +16,28 @@ "version-info" ] }, - "source": [] + "source": [ + "
\n", + "Package versions\n", + "\n", + "The code on this page was developed using the following requirements.\n", + "We recommend using these versions or newer.\n", + "\n", + "```\n", + "qiskit[all]~=1.2.4\n", + "qiskit-ibm-runtime~=0.33.2\n", + "qiskit-aer~=0.15.1\n", + "qiskit-serverless~=0.18.0\n", + "qiskit-ibm-catalog~=0.2\n", + "qiskit-addon-sqd~=0.8.1\n", + "qiskit-addon-utils~=0.1.0\n", + "qiskit-addon-aqc-tensor~=0.1.2\n", + "qiskit-addon-obp~=0.1.0\n", + "scipy~=1.14.1\n", + "pyscf~=2.7.0\n", + "```\n", + "
" + ] }, { "cell_type": "markdown", @@ -33,15 +54,40 @@ "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:" + "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_remote.py` example from the [Write your first Qiskit Serverless program](./serverless-first-program) topic:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, + "id": "9e41cd2f-bce6-4c8a-8e44-537c18b3023c", + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [], + "source": [ + "# This cell is hidden from users, it just creates a new folder\n", + "from pathlib import Path\n", + "\n", + "Path(\"./source_files\").mkdir(exist_ok=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, "id": "475d82f0-15cc-4db3-b3b0-54b07822b2a0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing ./source_files/transpile_remote.py\n" + ] + } + ], "source": [ "%%writefile ./source_files/transpile_remote.py\n", "\n", @@ -71,11 +117,19 @@ ] }, { - "id": "74fdcd4a-01cd-46ca-aa24-2a8a3605346f", "cell_type": "code", - "execution_count": null, + "execution_count": 3, + "id": "74fdcd4a-01cd-46ca-aa24-2a8a3605346f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Appending to ./source_files/transpile_remote.py\n" + ] + } + ], "source": [ "%%writefile --append ./source_files/transpile_remote.py\n", "\n", @@ -104,11 +158,19 @@ ] }, { - "id": "ac99b4a0-4a42-4c43-869d-265344b70359", "cell_type": "code", - "execution_count": null, + "execution_count": 4, + "id": "ac99b4a0-4a42-4c43-869d-265344b70359", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Appending to ./source_files/transpile_remote.py\n" + ] + } + ], "source": [ "%%writefile --append ./source_files/transpile_remote.py\n", "\n", @@ -121,6 +183,172 @@ "save_result(results) # Overwrites any previously saved results" ] }, + { + "cell_type": "code", + "execution_count": 5, + "id": "757f81e9-3981-4358-a1a2-d3262c7cddcd", + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Appending to ./source_files/transpile_remote.py\n" + ] + } + ], + "source": [ + "%%writefile --append ./source_files/transpile_remote.py\n", + "# This cell is hidden from users. It checks the transpilation ran correctly.\n", + "from qiskit import QuantumCircuit\n", + "assert all(isinstance(result, QuantumCircuit) for result in results)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "df28a92c-3585-49f0-a2ea-828a34638684", + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'QUEUED')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'QUEUED')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'QUEUED')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'QUEUED')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'INITIALIZING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'INITIALIZING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Job completed successfully\n" + ] + } + ], + "source": [ + "# This cell is hidden from users.\n", + "# It uploads the serverless program and checks it runs.\n", + "\n", + "\n", + "def test_serverless_job(title, entrypoint):\n", + " # Import in function to stop them interfering with user-facing code\n", + " from qiskit.circuit.random import random_circuit\n", + " from qiskit_serverless import IBMServerlessClient, QiskitFunction\n", + " import time\n", + " import uuid\n", + "\n", + " title += \"_\" + uuid.uuid4().hex[:8]\n", + " serverless = IBMServerlessClient()\n", + " transpile_remote_demo = QiskitFunction(\n", + " title=title,\n", + " entrypoint=entrypoint,\n", + " working_dir=\"./source_files/\",\n", + " )\n", + " serverless.upload(transpile_remote_demo)\n", + " job = serverless.get(title).run(\n", + " circuit=random_circuit(3, 3),\n", + " circuit_list=[random_circuit(3, 3) for _ in range(3)],\n", + " backend=\"ibm_kyiv\",\n", + " optimization_level=1,\n", + " )\n", + " for retry in range(25):\n", + " time.sleep(5)\n", + " status = job.status()\n", + " if status == \"DONE\":\n", + " print(\"Job completed successfully\")\n", + " return\n", + " if status not in [\"QUEUED\", \"INITIALIZING\", \"RUNNING\", \"DONE\"]:\n", + " raise Exception(\n", + " f\"Unexpected job status '{status}'.\\nHere's the logs:\\n\"\n", + " + job.logs()\n", + " )\n", + " print(f\"Waiting for job (status '{status}')\")\n", + " raise Exception(\"Job did not complete in time\")\n", + "\n", + "\n", + "test_serverless_job(\n", + " title=\"transpile_remote_serverless_test\", entrypoint=\"transpile_remote.py\"\n", + ")" + ] + }, { "cell_type": "markdown", "id": "611fe030-4494-46b5-9ea1-9678ac513210", @@ -137,10 +365,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "cea90969-cfbf-4181-9ffa-524f3709dc69", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Appending to ./source_files/transpile_remote.py\n" + ] + } + ], "source": [ "%%writefile --append ./source_files/transpile_remote.py\n", "\n", @@ -152,118 +388,100 @@ " return None" ] }, - { - "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": {}, + "id": "55163053-2cd8-4e5d-8470-d08055a6f401", + "metadata": { + "tags": [ + "remove-cell" + ] + }, "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", - "from qiskit_serverless import IBMServerlessClient\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 = IBMServerlessClient()\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": [ + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'QUEUED')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'QUEUED')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'INITIALIZING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'INITIALIZING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, { - "data": { - "text/plain": [ - "['transpile_demo.tar']" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for job (status 'RUNNING')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Job completed successfully\n" + ] } ], "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", - "id": "ef649b2a-ed95-4dd2-89d9-61438faa7c1e", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile ./source_files/extract_tarfile.py\n", - "\n", - "import tarfile\n", - "from qiskit_serverless import IBMServerlessClient\n", - "\n", - "serverless = IBMServerlessClient(token=\"\")\n", - "files = serverless.files()\n", - "demo_file = files[0]\n", - "downloaded_tar = serverless.file_download(demo_file)\n", - "\n", - "\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." + "# This cell is hidden from users.\n", + "# It checks the distributed program works.\n", + "test_serverless_job(\n", + " title=\"transpile_remote_serverless_test\", entrypoint=\"transpile_remote.py\"\n", + ")" ] }, { diff --git a/scripts/config/notebook-testing.toml b/scripts/config/notebook-testing.toml index 6af53638592..a97b354d5eb 100644 --- a/scripts/config/notebook-testing.toml +++ b/scripts/config/notebook-testing.toml @@ -30,6 +30,7 @@ notebooks_normal_test = [ "docs/guides/operator-class.ipynb", "docs/guides/error-mitigation-and-suppression-techniques.ipynb", "docs/guides/serverless-first-program.ipynb", + "docs/guides/serverless-manage-resources.ipynb", "docs/guides/serverless-run-first-workload.ipynb", "docs/guides/specify-observables-pauli.ipynb", "docs/guides/qiskit-addons-aqc-get-started.ipynb", @@ -62,7 +63,6 @@ notebooks_that_submit_jobs = [ notebooks_no_mock = [ "docs/guides/get-started-with-primitives.ipynb", "docs/guides/hello-world.ipynb", - "docs/guides/serverless-manage-resources.ipynb", "docs/guides/noise-learning.ipynb", "docs/guides/qiskit-addons-obp-get-started.ipynb", ]