{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Passing input arguments to your QiskitFunction\n", "In this tutorial, we’ll make our Qiskit Function more flexible by allowing it to accept input arguments and return structured results. This way, you can reuse the same function for different configurations without modifying the code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Install dependencies\n", "\n", "Before we begin, make sure the required packages are installed in your notebook environment:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pip install \"qiskit==2.2.3\" \"qiskit-ibm-runtime==0.43.1\" \"qiskit-serverless==0.27.0\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prep Work\n", "\n", "### Write the function with arguments\n", "A `QiskitFunction` can receive arguments in the form of keyword pairs (for example: `backend_name=\"ibm_nairobi\", circuit=my_circuit`). At runtime, the function retrieves these inputs by calling `get_arguments()`. The serverless framework deserializes them and returns a Python dictionary.\n", "\n", "> ⚠ Only serializable arguments can be passed to the function. Qiskit uses QPY serialization for QuantumCircuit objects, which allows circuits to be safely transmitted and reconstructed. [`get_arguments()`](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.serializers.get_arguments.html#qiskit_serverless.serializers.get_arguments) must also be able to deserialize the object correctly.\n", "\n", "### Why pass the circuit instead of building it inside the function?\n", "This approach makes the function reusable for different inputs and keeps its responsibility focused on execution steps: transpile → run → save results.\n", "\n", "Now, create a new file called `function_with_arguments.py` inside `source_files` [./source_files/function_with_arguments.py](./source_files/function_with_arguments.py). This function will:\n", "\n", " - Accept arguments (for example, a `QuantumCircuit` and a backend name).\n", " - Resolve a backend (fake or real) and transpile the circuit for it.\n", " - Run the Sampler primitive.\n", " - [Save the results](https://qiskit.github.io/qiskit-serverless/stubs/qiskit_serverless.core.save_result.html#qiskit_serverless.core.save_result) so they’re available via `job.result()` later.\n", "\n", "```python\n", "\"\"\"function with arguments for jupyter notebook.\"\"\"\n", "import os\n", "from qiskit import QuantumCircuit\n", "from qiskit.providers.exceptions import QiskitBackendNotFoundError\n", "from qiskit.transpiler import generate_preset_pass_manager\n", "from qiskit_ibm_runtime import QiskitRuntimeService\n", "from qiskit_ibm_runtime.fake_provider import FakeProviderForBackendV2\n", "from qiskit_ibm_runtime import SamplerV2 as Sampler\n", "from qiskit_serverless import get_arguments, save_result\n", "\n", "\n", "# ----- parse inputs -----\n", "# get all arguments passed to this function\n", "print(\"[main] Parsing arguments...\")\n", "arguments = get_arguments()\n", "\n", "# Extract inputs we care about\n", "circuit = arguments.get(\"circuit\")\n", "backend_name = arguments.get(\"backend_name\")\n", "service = arguments.get(\"service\")\n", "print(f\"[main] Inputs received (backend_name={backend_name})\")\n", "\n", "# Basic validation\n", "if not isinstance(circuit, QuantumCircuit):\n", " raise ValueError(\"circuit must be QuantumCircuit.\")\n", "if not isinstance(backend_name, str) or len(backend_name) == 0:\n", " raise ValueError(\"backend_name must be a non-empty string.\")\n", "\n", "print(f\"[main] Inputs received (backend_name={backend_name})\")\n", "\n", "# Choose a provider: fake provider for local testing, or a real servic\n", "if \"fake\" in backend_name.lower():\n", " print(\n", " \"[main] Using fake provider (auto-selected because backend_name contains 'fake').\"\n", " )\n", " service = FakeProviderForBackendV2()\n", "\n", "if isinstance(service, (FakeProviderForBackendV2, QiskitRuntimeService)):\n", " try:\n", " backend = service.backend(backend_name)\n", " print(f\"[main] Backend resolved (name={backend.name})\")\n", " except QiskitBackendNotFoundError as e:\n", " raise ValueError(f\"Error retrieving backend {backend_name}: {e}\") from e\n", "else:\n", " # Fallback: build a Runtime service from environment variables\n", " print(\n", " \"[main] No service provided and backend not fake; \"\n", " \"attempting to initialize QiskitRuntimeService from environment variables...\"\n", " )\n", "\n", " try:\n", " service = QiskitRuntimeService(\n", " channel=os.environ.get(\"QISKIT_IBM_CHANNEL\"),\n", " token=os.environ.get(\"QISKIT_IBM_TOKEN\"),\n", " instance=os.environ.get(\"QISKIT_IBM_INSTANCE\"),\n", " url=os.environ.get(\"QISKIT_IBM_URL\"),\n", " )\n", " backend = service.backend(backend_name)\n", " print(\n", " f\"[main] Runtime service initialized from env and backend \"\n", " f\"resolved (name={backend.name})\"\n", " )\n", " except QiskitBackendNotFoundError as e:\n", " raise ValueError(f\"The backend named {backend_name} couldn't be found.\") from e\n", " except Exception as e:\n", " raise ValueError(\n", " f\"`QiskitRuntimeService` couldn't be initialized with os environment variables: {e}.\"\n", " ) from e\n", "\n", "# ----- transpile -----\n", "# Match the run to the backend and transpile\n", "print(\"[main] Building preset pass manager (optimization_level=3)...\")\n", "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n", "print(\"[main] Transpiling circuit...\")\n", "isa_circuit = pm.run(circuit)\n", "print(\"[main] Transpilation complete\")\n", "\n", "# ----- execute -----\n", "# Execute and collect counts\n", "print(f\"[main] Executing circuit on backend (name={backend.name})...\")\n", "sampler = Sampler(backend)\n", "job_result = sampler.run([isa_circuit]).result()\n", "print(\"[main] Sampler execution complete\")\n", "\n", "# Extract counts\n", "quasi_dists = job_result[0].data.meas.get_counts()\n", "print(\"[main] Extracted counts from result\")\n", "\n", "# ----- persist -------\n", "# saving results of the execution\n", "save_result({\"quasi_dists\": quasi_dists})\n", "print(\"[main] Results saved.\")\n", "\n", "```\n", "\n", "This function resolves the backend through a `QiskitRuntimeService`. For real IBM Quantum backends, the service can be provided explicitly as an argument (recommended for notebooks and local runs) or constructed dynamically from environment variables. These variables—such as `QISKIT_IBM_CHANNEL`, `QISKIT_IBM_TOKEN`, `QISKIT_IBM_INSTANCE`, and `QISKIT_IBM_URL` —are typically set by your DevOps or cloud team during deployment (for example, in Kubernetes manifests or Docker Compose). When present, the function uses them to authenticate and configure the runtime service without hardcoding credentials in the code.\n", "For more information visit [Cloud infrastructure setup](https://qiskit.github.io/qiskit-serverless/deployment/cloud.html#)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deploying the function\n", "\n", "Now that we have a function that accepts arguments, we need to upload it to the serverless gateway and run it with custom inputs.\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, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "QiskitFunction(function-with-arguments)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit_serverless import ServerlessClient, QiskitFunction\n", "import os\n", "\n", "client = ServerlessClient(\n", " token=os.environ.get(\"GATEWAY_TOKEN\", \"awesome_token\"),\n", " instance=os.environ.get(\"GATEWAY_INSTANCE\", \"an_awesome_crn\"),\n", " host=os.environ.get(\"GATEWAY_HOST\", \"http://localhost:8000\"),\n", " # If you are using the kubernetes approach the URL must be http://localhost\n", ")\n", "\n", "function_with_args = QiskitFunction(\n", " title=\"function-with-arguments\",\n", " entrypoint=\"function_with_arguments.py\",\n", " working_dir=\"./source_files/\",\n", ")\n", "\n", "client.upload(function_with_args)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running the QiskitFunction\n", "\n", "### Testing environment\n", "After deploying `function-with-arguments`, let’s test it. For quick, credential‑free testing we’ll use a simulator. In this example we choose `FakeVigoV2` and pass its name as the `backend_name`. (The function detects “fake” in the name and constructs a fake provider internally.)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
┌───┐ ░ ┌─┐ \n",
" q_0: ┤ H ├──■───░─┤M├───\n",
" └───┘┌─┴─┐ ░ └╥┘┌─┐\n",
" q_1: ─────┤ X ├─░──╫─┤M├\n",
" └───┘ ░ ║ └╥┘\n",
"meas: 2/══════════════╩══╩═\n",
" 0 1 "
],
"text/plain": [
" ┌───┐ ░ ┌─┐ \n",
" q_0: ┤ H ├──■───░─┤M├───\n",
" └───┘┌─┴─┐ ░ └╥┘┌─┐\n",
" q_1: ─────┤ X ├─░──╫─┤M├\n",
" └───┘ ░ ║ └╥┘\n",
"meas: 2/══════════════╩══╩═\n",
" 0 1 "
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from qiskit import QuantumCircuit\n",
"from qiskit_ibm_runtime.fake_provider import FakeVigoV2\n",
"\n",
"backend_name = FakeVigoV2().name # e.g., \"fake_vigo\"\n",
"\n",
"circuit = QuantumCircuit(2)\n",
"circuit.h(0)\n",
"circuit.cx(0, 1)\n",
"circuit.measure_all()\n",
"circuit.draw()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run the function on the simulator backend:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"