Issue 479 | Documentation refactor (#481)

* Issue 479 | Docs: update client readme for pypi + getting started refactor

* Issue 479 | Docs: structure + remove old notebooks

* Issue 479 | Docs: setup

* Issue 479 | Docs: running

* Issue 479 | Client: job results deserialize

* Issue 479 | Docs: whys and ordering

* Issue 479 | Docs: VQE example + structure modification

* Issue 479 | Docs: QAOA example

* Issue 479 | Docs: short description + github refs

* Issue 479 | Docs: fix grammar
This commit is contained in:
Iskandar Sitdikov 2023-05-05 14:53:55 -04:00 committed by GitHub
parent 441fa167b8
commit 28ef68da9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 2110 additions and 3626 deletions

View File

@ -7,69 +7,119 @@
# Quantum Serverless client
Client part of quantum serverless project.
Installable python library to communicate with provisioned infrastructure.
![diagram](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/images/qs_diagram.png)
### Table of Contents
1. [Installation](#installation)
2. [Usage](#usage)
----------------------------------------------------------------------------------------------------
### Installation
# Installation
```shell
pip install quantum_serverless
```
or local installation from source
## Documentation
```shell
pip install -e .
```
Full docs can be found at https://qiskit-extensions.github.io/quantum-serverless/
----------------------------------------------------------------------------------------------------
## Usage
### Usage
### Step 1: write program
```python
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import Estimator
from quantum_serverless import distribute_task, get, get_arguments, save_result
from quantum_serverless import QuantumServerless, run_qiskit_remote, get, put
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.primitives import Sampler
from qiskit.quantum_info import SparsePauliOp
# 1. let's annotate out function to convert it
# to function that can be executed remotely
# using `run_qiskit_remote` decorator
@run_qiskit_remote()
def my_function(circuit: QuantumCircuit, obs: SparsePauliOp):
return Estimator().run([circuit], [obs]).result().values
# 1. let's annotate out function to convert it
# to distributed async function
# using `distribute_task` decorator
@distribute_task()
def distributed_sample(circuit: QuantumCircuit):
"""Calculates quasi dists as a distributed function."""
return Sampler().run(circuit).result().quasi_dists[0]
# 2. Next let's create out serverless object to control
# where our remote function will be executed
serverless = QuantumServerless()
# 2. our program will have one arguments
# `circuits` which will store list of circuits
# we want to sample in parallel.
# Let's use `get_arguments` funciton
# to access all program arguments
arguments = get_arguments()
circuits = arguments.get("circuits", [])
circuits = [random_circuit(2, 2) for _ in range(3)]
# 3. run our functions in a loop
# and get execution references back
function_references = [
distributed_sample(circuit)
for circuit in circuits
]
# 3. create serverless context
with serverless:
# 4. let's put some shared objects into remote storage that will be shared among all executions
obs_ref = put(SparsePauliOp(["ZZ"]))
# 4. `get` function will collect all
# results from distributed functions
collected_results = get(function_references)
# 4. run our function and get back reference to it
# as now our function it remote one
function_reference = my_function(circuits[0], obs_ref)
# 4.1 or we can run N of them in parallel (for all circuits)
function_references = [my_function(circ, obs_ref) for circ in circuits]
# 5. to get results back from reference
# we need to call `get` on function reference
print("Single execution:", get(function_reference))
print("N parallel executions:", get(function_references))
# 5. `save_result` will save results of program execution
# so we can access it later
save_result({
"quasi_dists": collected_results
})
```
### Step 2: run program
```python
from quantum_serverless import QuantumServerless, GatewayProvider
from qiskit.circuit.random import random_circuit
serverless = QuantumServerless(GatewayProvider(
username="<USERNAME>",
password="<PASSWORD>",
host="<GATEWAY_ADDRESS>",
))
# create program
program = Program(
title="Quickstart",
entrypoint="program.py",
working_dir="./src"
)
# create inputs to our program
circuits = []
for _ in range(3):
circuit = random_circuit(3, 2)
circuit.measure_all()
circuits.append(circuit)
# run program
job = serverless.run_program(
program=program,
arguments={
"circuits": circuits
}
)
```
### Step 3: monitor job status
```python
job.status()
# <JobStatus.SUCCEEDED: 'SUCCEEDED'>
# or get logs
job.logs()
```
### Step 4: get results
```python
job.result()
# {"quasi_dists": [
# {"0": 0.25, "1": 0.25, "2": 0.2499999999999999, "3": 0.2499999999999999},
# {"0": 0.1512273969460124, "1": 0.0400459556274728, "6": 0.1693190975212014, "7": 0.6394075499053132},
# {"0": 0.25, "1": 0.25, "4": 0.2499999999999999, "5": 0.2499999999999999}
# ]}
```

View File

@ -10,7 +10,14 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Quantum serverless."""
"""
.. currentmodule:: quantum_serverless
.. autosummary::
:toctree: ../stubs/
QuantumServerless
"""
from importlib_metadata import version as metadata_version, PackageNotFoundError

View File

@ -27,8 +27,11 @@ Core abstractions
:toctree: ../stubs/
Provider
GatewayProvider
KuberayProvider
ComputeResource
save_result
distribute_task
run_qiskit_remote
get

View File

@ -242,7 +242,8 @@ class GatewayJobClient(BaseJobClient):
timeout=REQUESTS_TIMEOUT,
)
if response.ok:
result = json.loads(response.text).get("result", None)
json_result = json.loads(response.text).get("result", "{}")
result = json.loads(json_result or "{}")
else:
logging.warning(
"Something went wrong during job result fetching. %s", response.text

View File

@ -23,7 +23,8 @@ Quantum serverless program serializers
.. autosummary::
:toctree: ../stubs/
QiskitObjectsDecoder
QiskitObjectsEncoder
"""
import json
import os

View File

@ -1,11 +1,13 @@
.. quantum_serverless:
.. module:: quantum_serverless
=================================
Quantum serverless API References
=================================
.. automodule:: quantum_serverless
:no-members:
:no-inherited-members:
:no-special-members:
.. toctree::
:maxdepth: 1

View File

@ -44,6 +44,17 @@ modindex_common_prefix = ["quantum_serverless_project."]
# html theme options
html_static_path = ["_static"]
# html_logo = "_static/images/logo.png"
html_theme_options = {
'github_button': True,
'github_user': 'Qiskit-Extensions',
'github_repo': 'quantum-serverless',
'github_type': 'star',
'github_count': False,
'extra_nav_links': {
'Repository': 'https://github.com/Qiskit-Extensions/quantum-serverless',
'Report issues': 'https://github.com/Qiskit-Extensions/quantum-serverless/issues/new?assignees=&labels=bug&template=bug_report.md'
}
}
# autodoc/autosummary options
autosummary_generate = True
@ -55,3 +66,4 @@ nbsphinx_timeout = 180
nbsphinx_execute = "never"
nbsphinx_widgets_path = ""
exclude_patterns = ["_build", "**.ipynb_checkpoints"]

View File

@ -0,0 +1,47 @@
====================
Client configuration
====================
Quantum Serverless has a client-server architecture,
which means that in order to interact with computational
resources, you need to have the client library
installed on your machine and configured to communicate with the provider.
To install the client library, run:
.. code-block::
pip install quantum_serverless
Next, we need to configure the client to communicate with the provider.
This is done through the `GatewayProvider` configuration.
Before we can configure the client and provider,
we need to know two things: the `username/password`
(authentication details) and the `host` of our gateway server.
If you are using the local docker-compose setup,
your username and password would be `user` and `password123`,
respectively, and the host would be `http://gateway:8000`.
If you are using a cloud deployment, your cloud administrator
will provide you with the details of the host and authentication.
Once you have all the necessary information,
you can start configuring the client:
.. code-block::
from quantum_serverless import QuantumServerless, GatewayProvider
provider = GatewayProvider(
username="<USERNAME>",
password="<PASSWORD>",
host="<HOST>",
)
client = QuantumServerless(provider)
With this configuration in place, you can run your programs
against the provider.

View File

@ -1,6 +1,7 @@
####################################
Guide: multi cloud deployment
####################################
==========================
Cloud infrastructure setup
==========================
``Quantum Serverless`` is a project that contains different resources. One of the most important ones is the ``client``
that you can connect with local and non-local resources.

15
docs/deployment/index.rst Normal file
View File

@ -0,0 +1,15 @@
==========
Deployment
==========
This section provides instructions for setting up Quantum Serverless.
These procedures include deploying it to your local machine or to
cloud services, as well as setting up the client to communicate with
compute resources.
.. toctree::
:maxdepth: 1
local
cloud
client_configuration

29
docs/deployment/local.rst Normal file
View File

@ -0,0 +1,29 @@
==========================
Local infrastructure setup
==========================
To set up Quantum Serverless on your local machine, you will need to use `docker-compose`_.
.. _docker-compose: https://docs.docker.com/compose/
Once you have Docker and docker-compose installed, you can run the following command to set up the infrastructure:
.. code-block::
$ VERSION=<VERSION> docker-compose [--profile <PROFILE>] up
The available profiles are `full` and `jupyter`.
The jupyter profile installs core services and Jupyter Notebook,
while the full profile installs all core services,
Jupyter Notebook, and logging and monitoring systems.
Available version can be found in `GitHub releases`_ page.
.. _GitHub releases: https://github.com/Qiskit-Extensions/quantum-serverless/releases
For example, if you want to install version `0.0.7` with all services,
the command would be:
.. code-block::
$ VERSION=0.0.7 docker-compose --profile full up

View File

@ -0,0 +1,379 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Example: VQE program\n",
"\n",
"This tutorial will be demonstation of creating VQE Program as well as migration guide on how you can replicate IBM Quantum VQE custom runtime program.\n",
"\n",
"Let's first get information on what is VQE runtime program and what inputs and outputs it has.\n",
"\n",
"**Description** of runtime program is: Variational Quantum Eigensolver (VQE) to find the minimal eigenvalue of a Hamiltonian.\n",
"\n",
"**Inputs**:\n",
"\n",
"| name | type | description |\n",
"| ---- | ---- | ----------- |\n",
"| ansatz | object | A parameterized quantum circuit preparing the ansatz wavefunction for the VQE. It is assumed that all qubits are initially in the 0 state. | \n",
"|aux_operators|array| A list or dict (with strings as keys) of operators of type PauliSumOp to be evaluated at the final, optimized state.|\n",
"|initial_layout | [null,array,object] | Initial position of virtual qubits on the physical qubits of the quantum device. Default is None. |\n",
"| initial_parameters|[array,string]|Initial parameters of the ansatz. Can be an array or the string ``'random'`` to choose random initial parameters.|\n",
"|max_evals_grouped|integer|The maximum number of parameter sets that can be evaluated at once. Defaults to the minimum of 2 times the number of parameters, or 1000.|\n",
"|measurement_error_mitigation|boolean|Whether to apply measurement error mitigation in form of a complete measurement fitter to the measurements. Defaults to False.|\n",
"|operator|object|The Hamiltonian whose smallest eigenvalue we're trying to find. Should be PauliSumOp|\n",
"|optimizer|object|The classical optimizer used in to update the parameters in each iteration. Can be either any of Qiskit's Optimizer classes. If a dictionary, only SPSA and QN-SPSA are supported and the dictionary must specify the name and options of the optimizer, e.g. ``{'name': 'SPSA', 'maxiter': 100}``.|\n",
"|shots|integer|The number of shots used for each circuit evaluation. Defaults to 1024.|\n",
"\n",
"**Return values**\n",
"\n",
"| name | type | description |\n",
"| ---- | ---- | ----------- |\n",
"|aux_operator_eigenvalues|array|The expectation values of the auxiliary operators at the optimal state. This is a numpy array.|\n",
"|cost_function_evals|integer|The number of cost function (energy) evaluations.|\n",
"|eigenstate|object|The square root of sampling probabilities for each computational basis state of the circuit with optimal parameters.|\n",
"|eigenvalue|array|The estimated eigenvalue. This is a complex number.|\n",
"|optimal_parameters|null|Not supported at the moment, therefore ``None``.|\n",
"|optimal_point|array|The optimal parameter values found during the optimization. This is a numpy array.|\n",
"|optimal_value|number|The smallest value found during the optimization. Equal to the ``eigenvalue`` attribute. This is a float.|\n",
"|optimizer_evals|integer|The number of steps of the optimizer.|\n",
"|optimizer_history|object|A dictionary containing information about the function evaluations (not necessarily the actual parameter value!): the current evaluation count, the parameters, the energy and the standard deviation.|\n",
"|optimizer_time|number|The total time taken by the optimizer. This is a float.|\n",
"\n",
"We will also add optional `QiskitRuntimeService` as an argument to use that to access real devices.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With that information we can start drafting our program implementation in `vqe.py` file.\n",
"\n",
"What our program should do:\n",
"1. parse input arguments\n",
"2. create run_vqe function that accepts estimator instance, creates VQE and runs calculation\n",
"3. decide which estimator to use and run vqe\n",
" - if runtime service was passed then create a session and run `run_vqe` function\n",
" - if runtime service was not passed then use stantard qiskit estimator\n",
"4. save results from vqe\n",
"\n",
"Roughly our VQE program will look like this\n",
"\n",
"```python\n",
"# vqe.py\n",
"\n",
"import ...\n",
"\n",
"def run_vqe(\n",
" estimator: BaseEstimator,\n",
" ansatz: QuantumCircuit,\n",
" operator: PauliSumOp,\n",
" optimizer: Optimizer,\n",
" initial_parameters: Optional[np.ndarray] = None\n",
"):\n",
" vqe = VQE(\n",
" estimator,\n",
" ansatz,\n",
" optimizer,\n",
" initial_point=initial_parameters\n",
" )\n",
" return vqe.compute_minimum_eigenvalue(operator)\n",
"\n",
"\n",
"arguments = get_arguments()\n",
"\n",
"service = arguments.get(\"service\")\n",
"ansatz = arguments.get(\"ansatz\")\n",
"operator = arguments.get(\"operator\")\n",
"initial_parameters = arguments.get(\"initial_parameters\") \n",
"optimizer = ...\n",
"\n",
"...\n",
"\n",
"if service is not None:\n",
" # if we have service we need to open a session and create estimator\n",
" backend = arguments.get(\"backend\", \"ibmq_qasm_simulator\")\n",
" with Session(service=service, backend=backend) as session:\n",
" estimator = Estimator(session=session, options=options) # qiskit_ibm_runtime.Estimator\n",
" vqe_result = run_vqe( estimator=estimator, ...)\n",
"else:\n",
" # if we do not have a service let's use standart local estimator\n",
" estimator = QiskitEstimator() # qiskit.primitives.Estimator\n",
" vqe_result = run_vqe(estimator=estimator, ...)\n",
"\n",
"save_result({\n",
" \"cost_function_evals\": vqe_result.cost_function_evals,\n",
" \"eigenvalue\": vqe_result.eigenvalue,\n",
" \"optimal_point\": vqe_result.optimal_point.tolist(),\n",
" \"optimal_value\": vqe_result.optimal_value,\n",
" \"optimizer_evals\": vqe_result.optimizer_evals,\n",
" \"optimizer_time\": vqe_result.optimizer_time,\n",
" ...\n",
"})\n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At this point we have our program implemented. Now we need to actually run it. But before let's prepare input arguments from our VQE program."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'ansatz': <qiskit.circuit.library.n_local.real_amplitudes.RealAmplitudes at 0x7fe570ac5b10>,\n",
" 'aux_operators': None,\n",
" 'initial_layout': None,\n",
" 'initial_parameters': array([ 1.96607313, 0.6418064 , 2.83542552, 2.07848803, -2.14744829,\n",
" 2.94371138, -2.27965871, -2.74576187, -2.38276942, 1.4102545 ,\n",
" -2.74783675, -2.81548099]),\n",
" 'max_evals_grouped': None,\n",
" 'measurement_error_mitigation': None,\n",
" 'operator': PauliSumOp(SparsePauliOp(['IIII', 'IIIZ', 'IIZZ', 'IIZI', 'IZZI', 'IZZZ', 'ZXIX', 'IXZX', 'ZXZX', 'IXIX', 'ZZII', 'ZZIZ', 'IZIZ', 'ZZZZ', 'ZIZI'],\n",
" coeffs=[-0.8054406 +0.j, 0.17452525+0.j, -0.23287429+0.j, 0.12177673+0.j,\n",
" 0.17452525+0.j, 0.1696433 +0.j, 0.04502464+0.j, -0.04502464+0.j,\n",
" -0.04502464+0.j, 0.04502464+0.j, -0.23287429+0.j, 0.16680137+0.j,\n",
" 0.16680137+0.j, 0.17533917+0.j, 0.12177673+0.j]), coeff=1.0),\n",
" 'optimizer': 'spsa',\n",
" 'shots': None,\n",
" 'service': None}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import numpy as np\n",
"\n",
"from qiskit.circuit.library import RealAmplitudes\n",
"\n",
"from qiskit_nature.second_q.drivers import PySCFDriver\n",
"from qiskit_nature.second_q.mappers import QubitConverter, ParityMapper\n",
"from qiskit_ibm_runtime import QiskitRuntimeService, Estimator, Session, Options\n",
"\n",
"USE_RUNTIME_SERVICE = False\n",
"\n",
"ansatz = RealAmplitudes(num_qubits=4, reps=2)\n",
"initial_point = np.random.uniform(-np.pi, np.pi, 12)\n",
"driver = PySCFDriver(\n",
" atom=\"H 0 0 0; H 0 0 0.72\" # Two Hydrogen atoms, 0.72 Angstrom apart\n",
")\n",
"molecule = driver.run()\n",
"qubit_converter = QubitConverter(ParityMapper())\n",
"hamiltonian = qubit_converter.convert(molecule.second_q_ops()[0])\n",
"\n",
"service = None\n",
"if USE_RUNTIME_SERVICE:\n",
" service = QiskitRuntimeService()\n",
" backend = \"ibmq_qasm_simulator\"\n",
"\n",
"input_arguments = {\n",
" \"ansatz\": RealAmplitudes(num_qubits=4, reps=2),\n",
" \"aux_operators\": None,\n",
" \"initial_layout\": None,\n",
" \"initial_parameters\": initial_point,\n",
" \"max_evals_grouped\": None,\n",
" \"measurement_error_mitigation\": None,\n",
" \"operator\": hamiltonian,\n",
" \"optimizer\": \"spsa\",\n",
" \"shots\": None,\n",
" \"service\": service\n",
"}\n",
"\n",
"input_arguments"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With arguments prepared we can create our quantum serverless client, setup provider and run our program"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 95e03817-663a-42fa-b199-0ee5016d2d49>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"VQE\",\n",
" entrypoint=\"vqe.py\",\n",
" working_dir=\"./source_files/vqe/\"\n",
")\n",
"\n",
"job = serverless.run_program(program, arguments=input_arguments)\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['243: -1.252362175485805',\n",
" '244: -1.245728828896075',\n",
" '245: -1.2505250649172739',\n",
" '246: -1.2572001670623152',\n",
" '247: -1.2611976192184255',\n",
" '248: -1.258139706050951',\n",
" '249: -1.2322759435538633',\n",
" '250: -1.224270224547061',\n",
" '251: -1.2645989534506294']"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.logs().split(\"\\n\")[-10:-1]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'aux_operator_eigenvalues': [],\n",
" 'cost_function_evals': 200,\n",
" 'eigenstate': None,\n",
" 'eigenvalue': -1.2645989534506294,\n",
" 'optimal_parameters': None,\n",
" 'optimal_point': [3.0276372310610675,\n",
" 0.13553102047367085,\n",
" 2.5087090109567773,\n",
" 1.8492042571718177,\n",
" -2.222021041944339,\n",
" 3.5428224235643113,\n",
" -1.2788913910370245,\n",
" -2.8246845124804763,\n",
" -2.728535775675822,\n",
" 3.4434351915865857,\n",
" -1.9083062252923417,\n",
" -1.7416966890283174],\n",
" 'optimal_value': -1.2645989534506294,\n",
" 'optimizer_evals': None,\n",
" 'optimizer_history': {},\n",
" 'optimizer_time': 0.8049108982086182}"
]
},
"execution_count": 8,
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,308 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Example: QAOA program\n",
"\n",
"This tutorial will be demonstation of creating QAOA Program as well as migration guide on how you can replicate IBM Quantum QAOA custom runtime program.\n",
"\n",
"Let's first get information on what is QAOA runtime program and what inputs and outputs it has. We will not be implementing full set of input/outputs, but we will cover most important ones. Later on we can recover full functionality if needed.\n",
"\n",
"**Description**: Qiskit Runtime QAOA program.\n",
"\n",
"\n",
"\n",
"\n",
"**Inputs**:\n",
"\n",
"| name | type | description |\n",
"| ---- | ---- | ----------- |\n",
"|initial_point|[array,string]|Initial parameters of the ansatz. Can be an array or the string ``'random'`` to choose random initial parameters. The type must be numpy.ndarray or str.|\n",
"|operator|object|The cost Hamiltonian, consisting of Pauli I and Z operators, whose smallest eigenvalue we're trying to find. The type must be a PauliSumOp.|\n",
"|optimization_level|integer|The optimization level to run if the swap strategies are not used. This value is 1 by default. This is an integer.|\n",
"|optimizer|object|The classical optimizer used to update the parameters in each iteration. Per default, SPSA with automatic calibration of the learning rate is used. The type must be a qiskit.algorithms.optimizers.Optimizer.|\n",
"|reps|integer|The number of QAOA repetitions, i.e. the QAOA depth typically labeled p. This value defaults to 1. This is an integer.|\n",
"\n",
"**Return values**\n",
"\n",
"| name | type | description |\n",
"| ---- | ---- | ----------- |\n",
"|cost_function_evals|number|The number of cost function (energy) evaluations. This is an integer.|\n",
"|eigenstate|object|The square root of sampling probabilities for each computational basis state of the circuit with optimal parameters.|\n",
"|eigenvalue|number|The estimated smallest eigenvalue.|\n",
"|optimal_parameters|null|Not supported at the moment, therefore ``None``.|\n",
"|optimal_point|array|The optimal parameter values found during the optimization.|\n",
"|optimal_value|number|The smallest value found during the optimization. Equal to the ``eigenvalue`` attribute.|\n",
"|optimizer_time|number|The total time taken by the optimizer.|\n",
"\n",
"\n",
"We will also add optional `QiskitRuntimeService` as an argument to use that to access real devices.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With that information we can start drafting our program implementation in `qaoa.py` file.\n",
"\n",
"What our program should do:\n",
"1. parse input arguments\n",
"2. create run_qaoa function that accepts estimator instance, creates VQE and runs calculation\n",
"3. decide which sampler to use and run vqe\n",
" - if runtime service was passed then create a session and run `run_qaoa` function\n",
" - if runtime service was not passed then use stantard qiskit sampler\n",
"4. save results from qaoa\n",
"\n",
"Roughly our QAOA program will look like this\n",
"\n",
"```python\n",
"# qaoa.py\n",
"\n",
"import ...\n",
"\n",
"def run_qaoa(sampler: BaseSampler, optimizer: Optimizer, reps: int, operator: PauliSumOp):\n",
" qaoa = QAOA(sampler, optimizer, reps=reps)\n",
" return qaoa.compute_minimum_eigenvalue(operator)\n",
"\n",
"\n",
"arguments = get_arguments()\n",
"service = arguments.get(\"service\")\n",
"operator = arguments.get(\"operator\")\n",
"initial_point = arguments.get(\"initial_point\")\n",
"reps = arguments.get(\"reps\", 1)\n",
"optimizer = ...\n",
"...\n",
"if service is not None:\n",
" # if we have service we need to open a session and create sampler\n",
" service = arguments.get(\"service\") \n",
" backend = arguments.get(\"backend\", \"ibmq_qasm_simulator\")\n",
" with Session(service=service, backend=backend) as session:\n",
" sampler = Sampler(session=session, options=options)\n",
" result = run_qaoa(sampler, optimizer, reps, operator)\n",
"else:\n",
" # if we do not have a service let's use standart local sampler\n",
" sampler = QiskitSampler()\n",
" result = run_qaoa(sampler, optimizer, reps, operator)\n",
"\n",
"save_result({\n",
" \"cost_function_evals\": result.cost_function_evals,\n",
" \"eigenstate\": result.eigenstate,\n",
" \"eigenvalue\": result.eigenvalue,\n",
" \"optimal_parameters\": list(result.optimal_parameters.values()),\n",
" \"optimal_point\": result.optimal_point.tolist(),\n",
" \"optimal_value\": result.optimal_value,\n",
" \"optimizer_time\": result.optimizer_time\n",
"})\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At this point we have our program implemented. Now we need to actually run it. But before let's prepare input arguments from our QAOA program."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'initial_point': None,\n",
" 'operator': SparsePauliOp(['ZIIZ'],\n",
" coeffs=[1.+0.j]),\n",
" 'optimization_level': 1,\n",
" 'optimizer': 'cobyla',\n",
" 'reps': 2,\n",
" 'service': None,\n",
" 'backend': 'ibmq_qasm_simulator'}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from qiskit.quantum_info import SparsePauliOp\n",
"from qiskit_ibm_runtime import QiskitRuntimeService\n",
"\n",
"USE_RUNTIME_SERVICE = False\n",
"\n",
"service = None\n",
"backend = \"ibmq_qasm_simulator\"\n",
"if USE_RUNTIME_SERVICE:\n",
" service = QiskitRuntimeService()\n",
"\n",
"input_arguments = {\n",
" \"initial_point\": None,\n",
" \"operator\": SparsePauliOp(\"ZIIZ\"),\n",
" \"optimization_level\": 1,\n",
" \"optimizer\": \"cobyla\",\n",
" \"reps\": 2,\n",
" \"service\": service,\n",
" \"backend\": backend\n",
"}\n",
"input_arguments"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 8cd76a64-64ea-468f-a295-114dcc5abd2a>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"QAOA\",\n",
" entrypoint=\"qaoa.py\",\n",
" working_dir=\"./source_files/qaoa/\"\n",
")\n",
"\n",
"job = serverless.run_program(program, arguments=input_arguments)\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'cost_function_evals': 56,\n",
" 'eigenstate': {'0': 2.6425405e-09,\n",
" '1': 0.1249999973574594,\n",
" '2': 2.6425405e-09,\n",
" '3': 0.1249999973574594,\n",
" '4': 2.6425405e-09,\n",
" '5': 0.1249999973574594,\n",
" '6': 2.6425405e-09,\n",
" '7': 0.1249999973574594,\n",
" '8': 0.1249999973574594,\n",
" '9': 2.6425405e-09,\n",
" '10': 0.1249999973574594,\n",
" '11': 2.6425405e-09,\n",
" '12': 0.1249999973574594,\n",
" '13': 2.6425405e-09,\n",
" '14': 0.1249999973574594,\n",
" '15': 2.6425405e-09},\n",
" 'eigenvalue': -0.9999999577193509,\n",
" 'optimal_parameters': [-4.261262774122553,\n",
" -1.7722068719193094,\n",
" 2.7455687782210543,\n",
" 0.6701009936251439],\n",
" 'optimal_point': [-4.261262774122553,\n",
" -1.7722068719193094,\n",
" 2.7455687782210543,\n",
" 0.6701009936251439],\n",
" 'optimal_value': -0.9999999577193509,\n",
" 'optimizer_time': 0.46456098556518555}"
]
},
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -5,10 +5,12 @@
"id": "604850c4-053a-4b78-bff2-f5f0c9250ed8",
"metadata": {},
"source": [
"# Tutorial: benchmark program to stress test compute resources\n",
"# Example: complex workflows\n",
"\n",
"In order to test compute resources we need to have a benchmark.\n",
"\n",
"In this benchmark program we demonstrate parallel workflows, recursion, parameter handling, etc.\n",
"\n",
"Below you can see an execution graph. Each of these boxes is a task in a program. This benchmark is designed to tests vertical and horizontal scalability of compute resources.\n",
"\n",
"![benchmark flow](./images/benchmark_flow.png)\n",
@ -41,7 +43,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 2,
"id": "3694d35b-6ad7-4a60-9edc-9c674be56908",
"metadata": {},
"outputs": [
@ -51,7 +53,7 @@
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 3,
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
@ -60,7 +62,7 @@
"gateway_provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://gateway:8000\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"serverless = QuantumServerless(gateway_provider)\n",
"serverless"
@ -68,17 +70,17 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 3,
"id": "030ccde4-d18b-43fc-979e-68df797f5977",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 1307c0d6-5a73-4452-bf9f-d31c5c397c38>"
"<Job | 192f4f1a-0611-40e8-addd-98bf6af8aa62>"
]
},
"execution_count": 5,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@ -103,7 +105,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 9,
"id": "316d3bdd-c9ed-404e-bf76-afca850920f5",
"metadata": {},
"outputs": [
@ -113,7 +115,7 @@
"'SUCCEEDED'"
]
},
"execution_count": 13,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}

View File

Before

Width:  |  Height:  |  Size: 752 KiB

After

Width:  |  Height:  |  Size: 752 KiB

View File

Before

Width:  |  Height:  |  Size: 720 KiB

After

Width:  |  Height:  |  Size: 720 KiB

View File

Before

Width:  |  Height:  |  Size: 708 KiB

After

Width:  |  Height:  |  Size: 708 KiB

View File

Before

Width:  |  Height:  |  Size: 715 KiB

After

Width:  |  Height:  |  Size: 715 KiB

View File

Before

Width:  |  Height:  |  Size: 715 KiB

After

Width:  |  Height:  |  Size: 715 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 716 KiB

After

Width:  |  Height:  |  Size: 716 KiB

View File

Before

Width:  |  Height:  |  Size: 712 KiB

After

Width:  |  Height:  |  Size: 712 KiB

View File

Before

Width:  |  Height:  |  Size: 780 KiB

After

Width:  |  Height:  |  Size: 780 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -23,8 +23,7 @@ from qiskit.algorithms.minimum_eigensolvers import VQE
from qiskit_nature.drivers import Molecule
from qiskit.algorithms.optimizers import SPSA
from quantum_serverless import distribute_task, get, QuantumServerless
from quantum_serverless.core import RedisStateHandler
from quantum_serverless import distribute_task, get, QuantumServerless, save_result
@distribute_task(target={"cpu": 2})
@ -189,8 +188,6 @@ def electronic_structure_problem(
if __name__ == '__main__':
serverless = QuantumServerless()
state_handler = RedisStateHandler("redis", 6379)
USE_RUNTIME = False
service = None
@ -221,12 +218,8 @@ if __name__ == '__main__':
use_local_simulator=not USE_RUNTIME
)
print("LiH experiment results:")
print("Energies: ", [e[0] for e in energies])
print("Shifts: ", [e[1] for e in energies])
print("Energy + shift: ", [e[0] + e[1] for e in energies])
state_handler.set("results", {
save_result({
"energies": [e[0] for e in energies],
"shifts": [e[1] for e in energies]
"shifts": [e[1] for e in energies],
"result": [e[0] + e[1] for e in energies]
})

View File

@ -0,0 +1,69 @@
import logging
from typing import Optional
import numpy as np
from qiskit import QuantumCircuit
from qiskit.primitives import BaseSampler, Sampler as QiskitSampler
from qiskit.algorithms.optimizers import SPSA, Optimizer, COBYLA
from qiskit.opflow import PauliSumOp
from qiskit.algorithms.minimum_eigensolvers import QAOA
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Session, Options
from quantum_serverless import QuantumServerless, distribute_task, get_arguments, get, save_result
def run_qaoa(
sampler: BaseSampler,
optimizer: Optimizer,
reps: int,
operator: PauliSumOp
):
qaoa = QAOA(sampler, optimizer, reps=reps)
return qaoa.compute_minimum_eigenvalue(operator)
if __name__ == '__main__':
arguments = get_arguments()
service = arguments.get("service")
operator = arguments.get("operator")
initial_point = arguments.get("initial_point")
reps = arguments.get("reps", 1)
optimizers = {
"spsa": SPSA(),
"cobyla": COBYLA()
}
optimizer = optimizers.get(arguments.get("optimizer", "nan"))
if optimizer is None:
raise Exception(
f"Optimizer {optimizer} is not is a list of available optimizers [{list(optimizers.keys())}]"
)
if service is not None:
# if we have service we need to open a session and create sampler
service = arguments.get("service")
backend = arguments.get("backend", "ibmq_qasm_simulator")
with Session(service=service, backend=backend) as session:
options = Options()
options.optimization_level = 3
sampler = Sampler(session=session, options=options)
result = run_qaoa(sampler, optimizer, reps, operator)
else:
# if we do not have a service let's use standart local sampler
sampler = QiskitSampler()
result = run_qaoa(sampler, optimizer, reps, operator)
save_result({
"cost_function_evals": result.cost_function_evals,
"eigenstate": result.eigenstate,
"eigenvalue": result.eigenvalue,
"optimal_parameters": list(result.optimal_parameters.values()),
"optimal_point": result.optimal_point.tolist(),
"optimal_value": result.optimal_value,
"optimizer_time": result.optimizer_time
})

View File

@ -0,0 +1,94 @@
import logging
from typing import Optional
import numpy as np
from qiskit import QuantumCircuit
from qiskit.primitives import BaseEstimator, Estimator as QiskitEstimator
from qiskit.algorithms.optimizers import SPSA, Optimizer
from qiskit.opflow import PauliSumOp
from qiskit.algorithms.minimum_eigensolvers import VQE
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator, Session, Options
from quantum_serverless import QuantumServerless, distribute_task, get_arguments, get, save_result
def run_vqe(
estimator: BaseEstimator,
ansatz: QuantumCircuit,
operator: PauliSumOp,
optimizer: Optimizer,
initial_parameters: Optional[np.ndarray] = None
):
vqe = VQE(
estimator,
ansatz,
optimizer,
initial_point=initial_parameters,
callback=lambda idx, parameters, mean, std: print(f"{idx}: {mean}")
)
return vqe.compute_minimum_eigenvalue(operator)
if __name__ == '__main__':
arguments = get_arguments()
service = arguments.get("service")
ansatz = arguments.get("ansatz")
operator = arguments.get("operator")
initial_parameters = arguments.get("initial_parameters")
optimizers = {
"spsa": SPSA()
}
optimizer = optimizers.get(arguments.get("optimizer", "nan"))
if optimizer is None:
raise Exception(
f"Optimizer {optimizer} is not is a list of available optimizers [{list(optimizers.keys())}]"
)
aux_operators = arguments.get("aux_operators")
if aux_operators is not None:
logging.warning("`aux_operators` are not supported yet.")
if service is not None:
# if we have service we need to open a session and create estimator
service = arguments.get("service")
backend = arguments.get("backend", "ibmq_qasm_simulator")
with Session(service=service, backend=backend) as session:
options = Options()
options.optimization_level = 3
estimator = Estimator(session=session, options=options)
vqe_result = run_vqe(
estimator=estimator,
ansatz=ansatz,
operator=operator,
optimizer=optimizer,
initial_parameters=initial_parameters
)
else:
# if we do not have a service let's use standart local estimator
estimator = QiskitEstimator()
vqe_result = run_vqe(
estimator=estimator,
ansatz=ansatz,
operator=operator,
optimizer=optimizer,
initial_parameters=initial_parameters
)
save_result({
"aux_operator_eigenvalues": [],
"cost_function_evals": vqe_result.cost_function_evals,
"eigenstate": None,
"eigenvalue": vqe_result.eigenvalue,
"optimal_parameters": None,
"optimal_point": vqe_result.optimal_point.tolist(),
"optimal_value": vqe_result.optimal_value,
"optimizer_evals": vqe_result.optimizer_evals,
"optimizer_history": {},
"optimizer_time": vqe_result.optimizer_time
})

View File

@ -0,0 +1,20 @@
===========
Development
===========
This section includes both end-to-end tutorials and short guides
that provide specific explanations of functions and features in Quantum Serverless.
**Examples**
.. toctree::
:maxdepth: 2
Examples <examples/index>
**Guides**
.. toctree::
:maxdepth: 2
Guides <guides/index>

View File

@ -1,443 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3ff90cec-1b42-4744-9400-eb5a4bd1ef3a",
"metadata": {},
"source": [
"# Getting started - level 2\n",
"\n",
"In this tutorial we will explore a little bit more advanced example of a program that require some configuration, requirements setup, etc. \n",
"\n",
"Again we will start with writing code for our program and saving it to [./source_files/gs_level_2.py](./source_files/gs_level_2.py) file.\n",
"This time it will be VQE example from [Qiskit documentation](https://qiskit.org/documentation/nature/tutorials/07_leveraging_qiskit_runtime.html) and we also introduce dependency management and arguments to our programs.\n",
"\n",
"```python\n",
"# source_files/gs_level_2.py\n",
"\n",
"from quantum_serverless import get_arguments\n",
"\n",
"from qiskit_nature.units import DistanceUnit\n",
"from qiskit_nature.second_q.drivers import PySCFDriver\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit_nature.second_q.mappers import ParityMapper\n",
"from qiskit_nature.second_q.properties import ParticleNumber\n",
"from qiskit_nature.second_q.transformers import ActiveSpaceTransformer\n",
"from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver\n",
"from qiskit_nature.second_q.algorithms.ground_state_solvers import GroundStateEigensolver\n",
"from qiskit.circuit.library import EfficientSU2\n",
"import numpy as np\n",
"from qiskit.utils import algorithm_globals\n",
"from qiskit.algorithms.optimizers import SPSA\n",
"from qiskit.algorithms.minimum_eigensolvers import VQE\n",
"from qiskit.primitives import Estimator\n",
"\n",
"\n",
"def run(bond_distance: float = 2.5):\n",
" driver = PySCFDriver(\n",
" atom=f\"Li 0 0 0; H 0 0 {bond_distance}\",\n",
" basis=\"sto3g\",\n",
" charge=0,\n",
" spin=0,\n",
" unit=DistanceUnit.ANGSTROM,\n",
" )\n",
" problem = driver.run()\n",
"\n",
" active_space_trafo = ActiveSpaceTransformer(\n",
" num_electrons=problem.num_particles, num_spatial_orbitals=3\n",
" )\n",
" problem = active_space_trafo.transform(problem)\n",
" qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)\n",
"\n",
" ansatz = EfficientSU2(num_qubits=4, reps=1, entanglement=\"linear\", insert_barriers=True)\n",
"\n",
" np.random.seed(5)\n",
" algorithm_globals.random_seed = 5\n",
"\n",
"\n",
" optimizer = SPSA(maxiter=100)\n",
" initial_point = np.random.random(ansatz.num_parameters)\n",
"\n",
" estimator = Estimator()\n",
" local_vqe = VQE(\n",
" estimator,\n",
" ansatz,\n",
" optimizer,\n",
" initial_point=initial_point,\n",
" )\n",
"\n",
" local_vqe_groundstate_solver = GroundStateEigensolver(qubit_converter, local_vqe)\n",
" local_vqe_result = local_vqe_groundstate_solver.solve(problem)\n",
"\n",
" print(local_vqe_result)\n",
"\n",
"\n",
"arguments = get_arguments()\n",
"bond_length = arguments.get(\"bond_length\", 2.55)\n",
"print(f\"Running for bond length {bond_length}.\")\n",
"run(bond_length)\n",
"\n",
"```\n",
"\n",
"As you can see here we used couple of additional things compared to `getting started level 1`. \n",
"\n",
"First, we are introducing dependency management by using the `qiskit-nature` module and `pyscf` extension.\n",
"We also using `get_arguments` function to parse arguments to our program, which return dictionary of arguments. In this case argument is `bond_length`. This means that we can, re-run our program over different bond lengths and produce a dissociation curve.\n",
"\n",
"\n",
"Next we need to run this program. For that we need to import necessary modules and configure `QuantumServerless` client. We are doing so by providing name and host for deployed infrastructure."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "79434a17-1222-4d04-a81a-8140ed630ed6",
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "b6ec8969-8c3d-4b7f-8c4c-adc6dbb9c59f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\", # this username has already been defined in local docker setup and does not need to be changed\n",
" password=\"password123\", # this password has already been defined in local docker setup and does not need to be changed\n",
" host=\"http://gateway:8000\", # address of provider\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "markdown",
"id": "544f7c64-ae1e-4480-b5d0-93f0c335eccd",
"metadata": {},
"source": [
"In addition to that we will provide additional `dependencies` to our `Program` construction and `arguments` to `run_program` method.\n",
"- `dependencies` parameter will install provided libraries to run our script. Dependencies can be python libraries available on PyPi or any package source installable via pip package manager .\n",
"- `arguments` parameter is a dictionary with arguments that will be passed for script execution"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "3ee09b31-4c7f-4ff3-af8f-294e4256793e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 534debe6-3a1b-4aea-933f-cdc2d67863d8>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"Getting started program level 2\",\n",
" entrypoint=\"gs_level_2.py\",\n",
" working_dir=\"./source_files\",\n",
" dependencies=[\"qiskit-nature\", \"qiskit-nature[pyscf]\"]\n",
")\n",
"\n",
"job = serverless.run_program(\n",
" program=program, \n",
" arguments={\n",
" \"bond_length\": 2.55\n",
" }\n",
")\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "420f2711-b8c6-4bf9-8651-c9d098348467",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "7e3b0cc7-2f08-4b69-a266-bbbe4e9a6c59",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Running for bond length 2.55.\n",
"/tmp/ray/session_2023-04-27_10-58-53_142605_1/runtime_resources/working_dir_files/_ray_pkg_0a274edc02afd24a/gs_level_2.py:33: DeprecationWarning: The QubitConverter class is deprecated as of version 0.6.0 and will be removed no sooner than 3 months after the release . Instead you should directly use the QubitMapper instance which you used to pass into the QubitConverter as the first argument. Refer to the documentation of the qiskit_nature.second_q.mappers module for more information.\n",
" qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)\n",
"/home/ray/anaconda3/lib/python3.9/site-packages/qiskit_nature/deprecation.py:297: PauliSumOpDeprecationWarning: PauliSumOp is deprecated as of version 0.6.0 and support for them will be removed no sooner than 3 months after the release. Instead, use SparsePauliOp. You can switch to SparsePauliOp immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`.\n",
" return func(*args, **kwargs)\n",
"=== GROUND STATE ENERGY ===\n",
" \n",
"* Electronic ground state energy (Hartree): -8.211426461751\n",
" - computed part: -8.211426461751\n",
" - {name} extracted energy part: 0.0\n",
"~ Nuclear repulsion energy (Hartree): 0.622561424612\n",
"> Total ground state energy (Hartree): -7.588865037139\n",
" \n",
"=== MEASURED OBSERVABLES ===\n",
" \n",
" 0: # Particles: 3.997 S: 0.436 S^2: 0.626 M: 0.001\n",
" \n",
"=== DIPOLE MOMENTS ===\n",
" \n",
"~ Nuclear dipole moment (a.u.): [0.0 0.0 4.81880162]\n",
" \n",
" 0: \n",
" * Electronic dipole moment (a.u.): [0.0 0.0 1.532189806944]\n",
" - computed part: [0.0 0.0 1.532189806944]\n",
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
" > Dipole moment (a.u.): [0.0 0.0 3.286611813056] Total: 3.286611813056\n",
" (debye): [0.0 0.0 8.353733188774] Total: 8.353733188774\n",
" \n",
"\n"
]
}
],
"source": [
"print(job.logs())"
]
},
{
"cell_type": "markdown",
"id": "94e3f04f-09df-4bc0-9715-643523207516",
"metadata": {},
"source": [
"---\n",
"If you want to run this program with different bond length you can run it 3 times. Programs are asynchronous, therefore each of instance of program will be running in parallel."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "5f4d4317-bcc9-4e1a-942a-a38ca5331261",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<Job | ac223c88-9b84-472c-b77b-dd3c56cd0244>,\n",
" <Job | d206f2ea-7d6b-43fe-a294-4ddc953eeb11>,\n",
" <Job | 6c6999bb-d942-491e-8411-92f5f62f567d>]"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"jobs = []\n",
"\n",
"for bond_length in [2.55, 3.0, 3.55]:\n",
" program = Program(\n",
" title=f\"Groundstate with bond length {bond_length}\",\n",
" entrypoint=\"gs_level_2.py\",\n",
" working_dir=\"./source_files\",\n",
" dependencies=[\"qiskit-nature\", \"qiskit-nature[pyscf]\"]\n",
" )\n",
" jobs.append(serverless.run_program(program, { \"bond_length\": bond_length }))\n",
"\n",
"jobs"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "46fe3955-ac35-43d9-a5de-2a4e2cad1483",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"SUCCEEDED\n",
"SUCCEEDED\n",
"SUCCEEDED\n"
]
}
],
"source": [
"for job in jobs:\n",
" print(job.status())"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "0988d6b4-03a4-4c87-ad1f-5b0526a7527e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Running for bond length 2.55.\n",
"/tmp/ray/session_2023-04-27_10-58-53_142605_1/runtime_resources/working_dir_files/_ray_pkg_0a274edc02afd24a/gs_level_2.py:33: DeprecationWarning: The QubitConverter class is deprecated as of version 0.6.0 and will be removed no sooner than 3 months after the release . Instead you should directly use the QubitMapper instance which you used to pass into the QubitConverter as the first argument. Refer to the documentation of the qiskit_nature.second_q.mappers module for more information.\n",
" qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)\n",
"/home/ray/anaconda3/lib/python3.9/site-packages/qiskit_nature/deprecation.py:297: PauliSumOpDeprecationWarning: PauliSumOp is deprecated as of version 0.6.0 and support for them will be removed no sooner than 3 months after the release. Instead, use SparsePauliOp. You can switch to SparsePauliOp immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`.\n",
" return func(*args, **kwargs)\n",
"=== GROUND STATE ENERGY ===\n",
" \n",
"* Electronic ground state energy (Hartree): -8.211426461751\n",
" - computed part: -8.211426461751\n",
" - {name} extracted energy part: 0.0\n",
"~ Nuclear repulsion energy (Hartree): 0.622561424612\n",
"> Total ground state energy (Hartree): -7.588865037139\n",
" \n",
"=== MEASURED OBSERVABLES ===\n",
" \n",
" 0: # Particles: 3.997 S: 0.436 S^2: 0.626 M: 0.001\n",
" \n",
"=== DIPOLE MOMENTS ===\n",
" \n",
"~ Nuclear dipole moment (a.u.): [0.0 0.0 4.81880162]\n",
" \n",
" 0: \n",
" * Electronic dipole moment (a.u.): [0.0 0.0 1.532189806948]\n",
" - computed part: [0.0 0.0 1.532189806948]\n",
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
" > Dipole moment (a.u.): [0.0 0.0 3.286611813052] Total: 3.286611813052\n",
" (debye): [0.0 0.0 8.353733188766] Total: 8.353733188766\n",
" \n",
"\n",
"Running for bond length 3.0.\n",
"/tmp/ray/session_2023-04-27_10-58-53_142605_1/runtime_resources/working_dir_files/_ray_pkg_0a274edc02afd24a/gs_level_2.py:33: DeprecationWarning: The QubitConverter class is deprecated as of version 0.6.0 and will be removed no sooner than 3 months after the release . Instead you should directly use the QubitMapper instance which you used to pass into the QubitConverter as the first argument. Refer to the documentation of the qiskit_nature.second_q.mappers module for more information.\n",
" qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)\n",
"/home/ray/anaconda3/lib/python3.9/site-packages/qiskit_nature/deprecation.py:297: PauliSumOpDeprecationWarning: PauliSumOp is deprecated as of version 0.6.0 and support for them will be removed no sooner than 3 months after the release. Instead, use SparsePauliOp. You can switch to SparsePauliOp immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`.\n",
" return func(*args, **kwargs)\n",
"=== GROUND STATE ENERGY ===\n",
" \n",
"* Electronic ground state energy (Hartree): -8.124024370249\n",
" - computed part: -8.124024370249\n",
" - {name} extracted energy part: 0.0\n",
"~ Nuclear repulsion energy (Hartree): 0.52917721092\n",
"> Total ground state energy (Hartree): -7.594847159329\n",
" \n",
"=== MEASURED OBSERVABLES ===\n",
" \n",
" 0: # Particles: 3.998 S: 0.408 S^2: 0.575 M: 0.001\n",
" \n",
"=== DIPOLE MOMENTS ===\n",
" \n",
"~ Nuclear dipole moment (a.u.): [0.0 0.0 5.66917837]\n",
" \n",
" 0: \n",
" * Electronic dipole moment (a.u.): [0.0 0.0 2.918212152957]\n",
" - computed part: [0.0 0.0 2.918212152957]\n",
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
" > Dipole moment (a.u.): [0.0 0.0 2.750966217043] Total: 2.750966217043\n",
" (debye): [0.0 0.0 6.992258013928] Total: 6.992258013928\n",
" \n",
"\n",
"Running for bond length 3.55.\n",
"/tmp/ray/session_2023-04-27_10-58-53_142605_1/runtime_resources/working_dir_files/_ray_pkg_0a274edc02afd24a/gs_level_2.py:33: DeprecationWarning: The QubitConverter class is deprecated as of version 0.6.0 and will be removed no sooner than 3 months after the release . Instead you should directly use the QubitMapper instance which you used to pass into the QubitConverter as the first argument. Refer to the documentation of the qiskit_nature.second_q.mappers module for more information.\n",
" qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)\n",
"/home/ray/anaconda3/lib/python3.9/site-packages/qiskit_nature/deprecation.py:297: PauliSumOpDeprecationWarning: PauliSumOp is deprecated as of version 0.6.0 and support for them will be removed no sooner than 3 months after the release. Instead, use SparsePauliOp. You can switch to SparsePauliOp immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`.\n",
" return func(*args, **kwargs)\n",
"=== GROUND STATE ENERGY ===\n",
" \n",
"* Electronic ground state energy (Hartree): -8.049823374615\n",
" - computed part: -8.049823374615\n",
" - {name} extracted energy part: 0.0\n",
"~ Nuclear repulsion energy (Hartree): 0.447192009228\n",
"> Total ground state energy (Hartree): -7.602631365386\n",
" \n",
"=== MEASURED OBSERVABLES ===\n",
" \n",
" 0: # Particles: 3.999 S: 0.378 S^2: 0.520 M: 0.001\n",
" \n",
"=== DIPOLE MOMENTS ===\n",
" \n",
"~ Nuclear dipole moment (a.u.): [0.0 0.0 6.70852774]\n",
" \n",
" 0: \n",
" * Electronic dipole moment (a.u.): [0.0 0.0 5.391791772177]\n",
" - computed part: [0.0 0.0 5.391791772177]\n",
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
" > Dipole moment (a.u.): [0.0 0.0 1.316735967823] Total: 1.316735967823\n",
" (debye): [0.0 0.0 3.346808683509] Total: 3.346808683509\n",
" \n",
"\n"
]
}
],
"source": [
"for job in jobs:\n",
" print(job.logs())"
]
},
{
"cell_type": "markdown",
"id": "95a1d859-a089-4d7f-ad88-b2acae1ed66d",
"metadata": {},
"source": [
"---\n",
"Other way would be refactoring program file itself to accept list of bond length and run them in a loop inside a program.\n",
"If you want 3 independent results, then running 3 programs would be a better fit. But if you want to do some postprocessing after execution of multiple function, then refactoring program file to run 3 function and postprocess them would be better choice. But at the end it all boils down to user preference."
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,229 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "73944cc6-e22d-43ae-8716-78bb00360a0f",
"metadata": {},
"source": [
"# Getting started - level 3\n",
"\n",
"In this tutorial we will explore a little bit more advanced example of a program that require some configuration, requirements setup, etc. \n",
"\n",
"Again we will start with writing code for our program and saving it to [./source_files/gs_level_3.py](./source_files/gs_level_3.py) file.\n",
"This time, our program will run an estimator as a parallel function, computing the expectation value of a single observable over a set of random circuits. The results will be saved to a database, which means it will be stored in a formatted way and later on we can fetch results of or programs without looking at logs.\n",
"\n",
"```python\n",
"# source_files/gs_level_3.py\n",
"\n",
"from qiskit import QuantumCircuit\n",
"from qiskit.circuit.random import random_circuit\n",
"from qiskit.quantum_info import SparsePauliOp\n",
"from qiskit.primitives import Estimator\n",
"\n",
"from quantum_serverless import QuantumServerless, distribute_task, get, put, save_result\n",
"\n",
"# 1. let's annotate out function to convert it\n",
"# to function that can be executed remotely\n",
"# using `distribute_task` decorator\n",
"@distribute_task()\n",
"def my_function(circuit: QuantumCircuit, obs: SparsePauliOp):\n",
" \"\"\"Compute expectation value of an obs given a circuit\"\"\"\n",
" return Estimator().run([circuit], [obs]).result().values\n",
"\n",
"\n",
"# 2. Next let's create our serverless object that we will be using to create context\n",
"# which will allow us to run functions in parallel\n",
"serverless = QuantumServerless()\n",
"\n",
"circuits = [random_circuit(2, 2) for _ in range(3)]\n",
"\n",
"# 3. create serverless context which will allow us to run functions in parallel\n",
"with serverless.context():\n",
" # 4. The observable is the same for all expectation value calculations. So we can put that object into remote storage since it will be shared among all executions of my_function.\n",
" obs_ref = put(SparsePauliOp([\"ZZ\"]))\n",
"\n",
" # 5. we can run our function for a single input circuit \n",
" # and get back a reference to it as now our function is a remote one\n",
" function_reference = my_function(circuits[0], obs_ref)\n",
"\n",
" # 5.1 or we can run N of them in parallel (for all circuits)\n",
" # note: if we will be using real backends (QPUs) we should either use\n",
" # N separate backends to run them in parallel or\n",
" # one will be running after each other sequentially\n",
" function_references = [my_function(circ, obs_ref) for circ in circuits]\n",
"\n",
" # 6. to get results back from reference\n",
" # we need to call `get` on function reference\n",
" single_result = get(function_reference)\n",
" parallel_result = get(function_references)\n",
" print(\"Single execution:\", single_result)\n",
" print(\"N parallel executions:\", parallel_result)\n",
"\n",
" # 6.1 (Optional) write results to db.\n",
" save_result({\n",
" \"status\": \"ok\",\n",
" \"single\": single_result.tolist(),\n",
" \"parallel_result\": [entry.tolist() for entry in parallel_result]\n",
" })\n",
"\n",
"```\n",
"\n",
"As you can see we move to advanced section of using serverless. \n",
"\n",
"Here we are using `ditribute_task` decorator to convert our function to asynchronous distributed one. \n",
"With that `my_function` is converted into asynchronous distributed function (as a result you will be getting function pointer), which means that the function no longer executes as part of your local python process, but executed on configured compute resources.\n",
"\n",
"Moreover, we are using `save_result` function in order to save results into database storage, so we can retrieve it later after program execution.\n",
"\n",
"Next we need to run this program. For that we need to import necessary modules and configure QuantumServerless client. We are doing so by providing name and host for deployed infrastructure."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "9130c64a-1e7f-4d08-afff-b2905b2d95ad",
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "0f22daae-9f0e-4f7a-8a1f-5ade989d8be9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\", # this username has already been defined in local docker setup and does not need to be changed\n",
" password=\"password123\", # this password has already been defined in local docker setup and does not need to be changed\n",
" host=\"http://gateway:8000\", # address of provider\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "markdown",
"id": "3321b4a0-b60d-433a-992a-79e5868d309b",
"metadata": {},
"source": [
"Run program"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "f556dd85-35da-48d1-9ae1-f04a386544d9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 65dadf22-16d5-40e2-9a3e-de460e439c34>"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program \n",
"\n",
"program = Program(\n",
" title=\"Advanced program\",\n",
" entrypoint=\"gs_level_3.py\",\n",
" working_dir=\"./source_files/\"\n",
")\n",
"\n",
"job = serverless.run_program(program)\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "2de3fd64-9010-48d9-ac7c-f46a7b36ba81",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "markdown",
"id": "29336f0b-ffcf-4cdb-931c-11faf09f15ff",
"metadata": {},
"source": [
"With `job.result()` we can get saved results inside of our function back. `.result()` call will return you whatever you passed in `save_result` inside the program file, while `.logs()` will return everything that was logged by job (stdio, e.g prints)."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "1fb8931f-c8e2-49dd-923f-16fa3a7a5feb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"status\": \"ok\", \"single\": [1.0], \"parallel_result\": [[1.0], [1.0], [1.0]]}'"
]
},
"execution_count": 21,
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,61 +0,0 @@
from quantum_serverless import get_arguments
from qiskit_nature.units import DistanceUnit
from qiskit_nature.second_q.drivers import PySCFDriver
from qiskit_nature.second_q.mappers import QubitConverter
from qiskit_nature.second_q.mappers import ParityMapper
from qiskit_nature.second_q.properties import ParticleNumber
from qiskit_nature.second_q.transformers import ActiveSpaceTransformer
from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver
from qiskit_nature.second_q.algorithms.ground_state_solvers import GroundStateEigensolver
from qiskit.circuit.library import EfficientSU2
import numpy as np
from qiskit.utils import algorithm_globals
from qiskit.algorithms.optimizers import SPSA
from qiskit.algorithms.minimum_eigensolvers import VQE
from qiskit.primitives import Estimator
def run(bond_distance: float = 2.5):
driver = PySCFDriver(
atom=f"Li 0 0 0; H 0 0 {bond_distance}",
basis="sto3g",
charge=0,
spin=0,
unit=DistanceUnit.ANGSTROM,
)
problem = driver.run()
active_space_trafo = ActiveSpaceTransformer(
num_electrons=problem.num_particles, num_spatial_orbitals=3
)
problem = active_space_trafo.transform(problem)
qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)
ansatz = EfficientSU2(num_qubits=4, reps=1, entanglement="linear", insert_barriers=True)
np.random.seed(5)
algorithm_globals.random_seed = 5
optimizer = SPSA(maxiter=100)
initial_point = np.random.random(ansatz.num_parameters)
estimator = Estimator()
local_vqe = VQE(
estimator,
ansatz,
optimizer,
initial_point=initial_point,
)
local_vqe_groundstate_solver = GroundStateEigensolver(qubit_converter, local_vqe)
local_vqe_result = local_vqe_groundstate_solver.solve(problem)
print(local_vqe_result)
arguments = get_arguments()
bond_length = arguments.get("bond_length", 2.55)
print(f"Running for bond length {bond_length}.")
run(bond_length)

View File

@ -1,48 +0,0 @@
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Estimator
from quantum_serverless import QuantumServerless, distribute_task, get, put, save_result
# 1. let's annotate out function to convert it
# to function that can be executed remotely
# using `distribute_task` decorator
@distribute_task()
def my_function(circuit: QuantumCircuit, obs: SparsePauliOp):
"""Compute expectation value of an obs given a circuit"""
return Estimator().run([circuit], [obs]).result().values
# 2. Next let's create our serverless object that we will be using to create context
# which will allow us to run functions in parallel
serverless = QuantumServerless()
circuits = [random_circuit(2, 2) for _ in range(3)]
# 3. create serverless context which will allow us to run functions in parallel
with serverless.context():
# 4. The observable is the same for all expectation value calculations. So we can put that object into remote storage since it will be shared among all executions of my_function.
obs_ref = put(SparsePauliOp(["ZZ"]))
# 5. we can run our function for a single input circuit
# and get back a reference to it as now our function is a remote one
function_reference = my_function(circuits[0], obs_ref)
# 5.1 or we can run N of them in parallel (for all circuits)
function_references = [my_function(circ, obs_ref) for circ in circuits]
# 6. to get results back from reference
# we need to call `get` on function reference
single_result = get(function_reference)
parallel_result = get(function_references)
print("Single execution:", single_result)
print("N parallel executions:", parallel_result)
# 6.1 (Optional) write results to db.
save_result({
"status": "ok",
"single": single_result.tolist(),
"parallel_result": [entry.tolist() for entry in parallel_result]
})

View File

@ -1,257 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "bcdd01c6",
"metadata": {},
"source": [
"# Guide: run anything in distributed fashion"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "71a5d29e",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from typing import List\n",
"\n",
"from qiskit import QuantumCircuit, transpile\n",
"from qiskit.circuit.random import random_circuit\n",
"\n",
"from quantum_serverless import QuantumServerless, distribute_task, get, put"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "711f0a5e-047a-49e1-90be-a3a30a7b4d06",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [local]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"serverless = QuantumServerless()\n",
"\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "424affa8-9ffc-41c9-9b42-5e58221fdccd",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\"> ┌───┐ ┌───┐ \n",
"q_0: ──────────■───────────────┤ S ├─────────────┤ X ├──────────────\n",
" │ └───┘ ┌───────┴───┴────────┐ \n",
"q_1: ──────────X─────────────────■───────┤ U2(4.0568,0.20671) ├─────\n",
" │ ┌─┴─┐ └────────────────────┘ \n",
"q_2: ──────────X───────────────┤ X ├───────────────X────────────────\n",
" ┌─────┐ └─┬─┘ │ ┌───┐\n",
"q_3: ───────┤ Sdg ├──────────────■─────────────────┼───────────┤ T ├\n",
" ┌──────┴─────┴───────┐┌────────────┐ │ └───┘\n",
"q_4: ┤ U2(0.37458,3.2557) ├┤ Rz(1.7343) ├──────────X────────────────\n",
" └────────────────────┘└────────────┘ </pre>"
],
"text/plain": [
" ┌───┐ ┌───┐ \n",
"q_0: ──────────■───────────────┤ S ├─────────────┤ X ├──────────────\n",
" │ └───┘ ┌───────┴───┴────────┐ \n",
"q_1: ──────────X─────────────────■───────┤ U2(4.0568,0.20671) ├─────\n",
" │ ┌─┴─┐ └────────────────────┘ \n",
"q_2: ──────────X───────────────┤ X ├───────────────X────────────────\n",
" ┌─────┐ └─┬─┘ │ ┌───┐\n",
"q_3: ───────┤ Sdg ├──────────────■─────────────────┼───────────┤ T ├\n",
" ┌──────┴─────┴───────┐┌────────────┐ │ └───┘\n",
"q_4: ┤ U2(0.37458,3.2557) ├┤ Rz(1.7343) ├──────────X────────────────\n",
" └────────────────────┘└────────────┘ "
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"circuit = random_circuit(5, 3)\n",
"\n",
"circuit.draw()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "bb3f6d67-9019-416e-b801-a8bec031970e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Circuit reference: ObjectRef(00ffffffffffffffffffffffffffffffffffffff0100000002000000)\n",
"Retrieved object\n",
" ┌───┐ ┌───┐ \n",
"q_0: ──────────■───────────────┤ S ├─────────────┤ X ├──────────────\n",
" │ └───┘ ┌───────┴───┴────────┐ \n",
"q_1: ──────────X─────────────────■───────┤ U2(4.0568,0.20671) ├─────\n",
" │ ┌─┴─┐ └────────────────────┘ \n",
"q_2: ──────────X───────────────┤ X ├───────────────X────────────────\n",
" ┌─────┐ └─┬─┘ │ ┌───┐\n",
"q_3: ───────┤ Sdg ├──────────────■─────────────────┼───────────┤ T ├\n",
" ┌──────┴─────┴───────┐┌────────────┐ │ └───┘\n",
"q_4: ┤ U2(0.37458,3.2557) ├┤ Rz(1.7343) ├──────────X────────────────\n",
" └────────────────────┘└────────────┘ \n"
]
}
],
"source": [
"# object\n",
"\n",
"with serverless.context():\n",
" # make any object as remote object\n",
" circuit_reference = put(circuit)\n",
"\n",
" print(\"Circuit reference:\", circuit_reference)\n",
" \n",
" # to get any object from remote to local call `get` function\n",
" retrieved_object = get(circuit_reference)\n",
" print(\"Retrieved object\\n\", retrieved_object)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "ae461219-d128-4dd4-8b5f-b67f165d5bc9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[<qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdd70e60790>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdd70e60e10>],\n",
" [<qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdd70e602d0>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdd70e60d90>],\n",
" [<qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdd70e60b90>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdd70e60ed0>]]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"circuit_batches = [\n",
" [random_circuit(5, 3) for _ in range(2)]\n",
" for n_batch in range(3)\n",
"]\n",
"circuit_batches"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "0a0687f4-5fa2-4dd0-ba9d-7fd634dde295",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Function reference: ObjectRef(c8ef45ccd0112571ffffffffffffffffffffffff0100000001000000)\n",
"Function result: [<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e6db90>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e60750>]\n"
]
}
],
"source": [
"# task\n",
"\n",
"# in order to make any function a remote/parallel function annotate it with `remote` decorator\n",
"\n",
"@distribute_task()\n",
"def remote_transpile(circuits: List[QuantumCircuit]):\n",
" return transpile(circuits)\n",
"\n",
"with serverless.context():\n",
" # to call this function by arring `remote` to function call `<function_name>.remote`\n",
" remote_function_reference = remote_transpile(circuit_batches[0])\n",
" print(\"Function reference:\", remote_function_reference)\n",
" \n",
" # to get function results we again using `get`\n",
" result = get(remote_function_reference)\n",
" print(\"Function result:\", result)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "7b1b7aaa-de95-4796-9a8f-d1d5f278bc06",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Functions references: [ObjectRef(16310a0f0a45af5cffffffffffffffffffffffff0100000001000000), ObjectRef(c2668a65bda616c1ffffffffffffffffffffffff0100000001000000), ObjectRef(32d950ec0ccf9d2affffffffffffffffffffffff0100000001000000)]\n",
"Functions resulst: [[<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e61b50>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e77a10>], [<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e615d0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70dec5d0>], [<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e761d0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fdd70e61950>]]\n"
]
}
],
"source": [
"# we can run multiple remote function in parallel\n",
"\n",
"with serverless.context():\n",
" # to call this function by arring `remote` to function call `<function_name>.remote`\n",
" remote_function_references = [\n",
" remote_transpile(circuit_batch)\n",
" for circuit_batch in circuit_batches\n",
" ]\n",
" print(\"Functions references:\", remote_function_references)\n",
" \n",
" # to get function results we again using `get`\n",
" result = get(remote_function_references)\n",
" print(\"Functions resulst:\", 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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,129 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9d1f0479-58bc-4d3b-9cd6-ddae7c206f29",
"metadata": {},
"source": [
"# Guide: specific resource requirements"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "5c870446-9c6c-440c-9082-5ddad431e643",
"metadata": {},
"outputs": [],
"source": [
"from typing import List\n",
"\n",
"from qiskit import QuantumCircuit, transpile\n",
"from qiskit.circuit.random import random_circuit\n",
"\n",
"from quantum_serverless import QuantumServerless, distribute_task, get"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ff073fc9-1e24-4122-b968-032ee3d2d2f2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [local]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"serverless = QuantumServerless()\n",
"\n",
"serverless"
]
},
{
"cell_type": "markdown",
"id": "396815a2-ef95-433a-b134-9344d96ff027",
"metadata": {},
"source": [
"Sometimes your function has specific requirements. For example transpilation is heavy procedure and we want to make sure it will not fail, so we want to allocate more cpus for calculation. \n",
"\n",
"To allocate specific resource we can add `target` parameter to `run_qiskit_remote` decorator. This will tell scheduler to run this function with 2 cpus. "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e6b433b5-63dd-4e99-8b1c-472344c2e311",
"metadata": {},
"outputs": [],
"source": [
"@distribute_task(target={\"cpu\": 2})\n",
"def remote_transpile(circuits: List[QuantumCircuit]):\n",
" return transpile(circuits)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "96e18044-bd49-4d7b-baa4-9866da07d442",
"metadata": {},
"outputs": [],
"source": [
"circuits = [random_circuit(5, 2) for _ in range(3)]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "3ad5f102-5c48-4a08-a27f-e55a23131592",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fc27945cfd0>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fc208191910>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fc2793e9450>]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"with serverless.context():\n",
" results = get(remote_transpile(circuits))\n",
" \n",
"results"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,91 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Guide: Qiskit Runtime Primitives with QuantumServerless"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Single execution: [0.6825]\n",
"N parallel executions: [array([0.678]), array([1.]), array([1.])]\n"
]
}
],
"source": [
"from qiskit import QuantumCircuit\n",
"from qiskit.circuit.random import random_circuit\n",
"from qiskit.quantum_info import SparsePauliOp\n",
"from qiskit_ibm_runtime import Estimator\n",
"\n",
"from quantum_serverless import QuantumServerless, distribute_task, get, put\n",
"\n",
"# 1. let's annotate out function to convert it\n",
"# to function that can be executed remotely\n",
"# using `run_qiskit_remote` decorator\n",
"@distribute_task()\n",
"def my_function(circuit: QuantumCircuit, obs: SparsePauliOp):\n",
" return Estimator().run([circuit], [obs]).result().values\n",
"\n",
"\n",
"# 2. Next let's create out serverless object to control\n",
"# where our remote function will be executed\n",
"serverless = QuantumServerless()\n",
"\n",
"circuits = [random_circuit(2, 2) for _ in range(3)]\n",
"\n",
"# 3. create serverless context\n",
"with serverless.context():\n",
" # 4. let's put some shared objects into remote storage that will be shared among all executions\n",
" obs_ref = put(SparsePauliOp([\"ZZ\"]))\n",
"\n",
" # 4. run our function and get back reference to it\n",
" # as now our function it remote one\n",
" function_reference = my_function(circuits[0], obs_ref)\n",
"\n",
" # 4.1 or we can run N of them in parallel (for all circuits)\n",
" function_references = [my_function(circ, obs_ref) for circ in circuits]\n",
"\n",
" # 5. to get results back from reference\n",
" # we need to call `get` on function reference\n",
" print(\"Single execution:\", get(function_reference))\n",
" print(\"N parallel executions:\", get(function_references))"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -1,110 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3224646a-4cd3-4cd2-903d-876fc65fdbf5",
"metadata": {},
"source": [
"# Guide: specific package requirements\n"
]
},
{
"cell_type": "markdown",
"id": "5e0a43e6",
"metadata": {},
"source": [
"There are usual cases where you need to install packages on nodes. In order to do that we provide `pip` key with list of packages in `target` for `run_qiskit_remote` decorator."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "59f1086a-1f0f-404b-9293-f398bf4af555",
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"from quantum_serverless import QuantumServerless, distribute_task, get"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "d6b45b0f-a781-4824-8473-e4774c96b846",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [local]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"serverless = QuantumServerless()\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7b03ae2a-a78b-4c84-95d2-76d161778c7a",
"metadata": {},
"outputs": [],
"source": [
"@distribute_task(target={\"pip\": [\"requests\"]})\n",
"def call_ibm():\n",
" return requests.get(\"https://ibm.com\").status_code"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "370f30f3-6faa-4ac9-aa38-be132bdc0d69",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[200, 200]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"with serverless.context():\n",
" results = get([call_ibm() for _ in range(2)])\n",
" \n",
"results"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,270 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "83581f24-f41b-4d0a-81c5-70402812ce75",
"metadata": {},
"source": [
"# Guide: run async programs\n",
"\n",
"In this guide we will look into an ability of QuantumServerless running asynchronous programs as async jobs.\n",
"\n",
"Let's import all necessary dependencies and create `QuantumServerless` instance."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "5428848b-8014-41bc-b9b3-2be5d2f39671",
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, Program, GatewayProvider, Provider\n",
"from quantum_serverless.core import ComputeResource"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "6c5a72dd-8cd2-4d9a-bcc2-781649e25634",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gateway_provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://gateway:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless([gateway_provider])\n",
"serverless"
]
},
{
"cell_type": "markdown",
"id": "abe78b1e-ec3b-430a-b57f-b3262ff4ee93",
"metadata": {},
"source": [
"> NOTE: important thing to remember is that async jobs are only available with real cluster and will not work in local machine. \n",
"\n",
"In this example we will have head node running on inside docker on localhost\n",
"\n",
"---\n",
"\n",
"Now we have all prep steps figured out. Next step will be defining script for the job itself.\n",
"\n",
"Inside the script we will be using the same principles that we showed in other tutorials (creating QuantumServerless instance, annotating functions and getting results)\n",
"\n",
"```python\n",
"# program.py\n",
"from quantum_serverless import QuantumServerless, run_qiskit_remote, get\n",
"\n",
"\n",
"@distribute_task()\n",
"def ultimate():\n",
" return 42\n",
"\n",
"\n",
"with QuantumServerless().context():\n",
" result = get([\n",
" ultimate()\n",
" for _ in range(100)\n",
" ])\n",
" \n",
"print(result)\n",
"```\n",
"\n",
"[Program file](./program.py) will be located in the same directory as our notebook.\n",
"\n",
"\n",
"Let's run our script/job using `run_program` function provided by QuantumServerless.\n",
"Run program accepts `Program` instance."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "7612312e-91fc-4078-988b-4ffabfc9df63",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 2>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"program = Program(\n",
" title=\"awesome_program\",\n",
" entrypoint=\"program.py\",\n",
" working_dir=\"./\",\n",
" description=\"description of a program\",\n",
" version=\"0.0.1\"\n",
")\n",
"\n",
"job = serverless.run_program(program)\n",
"job"
]
},
{
"cell_type": "markdown",
"id": "d47ce9c3-ca88-473c-b73f-67abc0a7dd95",
"metadata": {},
"source": [
"You will get `Job` object as result, which you can query for `status` and `logs` to see status of your job and logs."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "e67a039f-5a0f-415c-991a-926f3fbc0cbe",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "3d22f21e-5d42-4917-800c-92de776b0b07",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Single execution: [0.]\n",
"N parallel executions: [array([0.]), array([1.]), array([0.])]\n",
"\n"
]
}
],
"source": [
"print(job.logs())"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "4a2f2347-a3b2-45da-800a-95021aaddca1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"single_execution\": [0.0], \"multiple_executions\": [[0.0], [1.0], [0.0]]}'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.result()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "1cc48d5e-6c89-48ab-9845-46fd9dddacd5",
"metadata": {},
"outputs": [],
"source": [
"# recover job by id\n",
"recovered_job = serverless.get_job_by_id(job.job_id)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "1218d690-7f95-417d-a5ed-636c75638a0d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"recovered_job.status()"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "d27c575a-29b6-46b4-919b-ed0507478e56",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"single_execution\": [0.0], \"multiple_executions\": [[0.0], [1.0], [0.0]]}'"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"recovered_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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,133 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "375c82d9-3592-4a5b-aa38-f76a5876c510",
"metadata": {},
"source": [
"# Guide: get finished remote tasks\n",
"\n",
"When you are working with remote functions you might face a situation when you do not need to wait for all remote tasks to complete, so `get` is not an option as it is waiting for all tasks for completion.\n",
"\n",
"For this situation we provide `get_refs_by_status` function, which separates references by readiness. \n",
"\n",
"Let's see in examples"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "d3b5af54-f83d-4b82-baa4-cc032c5b5050",
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"\n",
"from quantum_serverless import (\n",
" QuantumServerless, \n",
" get, \n",
" distribute_task,\n",
" get_refs_by_status\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "979c2a4c-3826-4470-b711-88259a951d68",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [local]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"@distribute_task()\n",
"def sleepy_function(sleep_time: int):\n",
" time.sleep(sleep_time)\n",
" return sleep_time\n",
"\n",
"serverless = QuantumServerless()\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "64d0ec49-5066-48de-8f1d-628e3964e6c8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Waiting for 7 remote functions to finish.\n",
"Waiting for 6 remote functions to finish.\n",
"Waiting for 5 remote functions to finish.\n",
"Waiting for 4 remote functions to finish.\n",
"Waiting for 3 remote functions to finish.\n",
"Waiting for 2 remote functions to finish.\n",
"Waiting for 1 remote functions to finish.\n",
"Waiting for 0 remote functions to finish.\n"
]
},
{
"data": {
"text/plain": [
"[4, 6, 7, 8, 3, 5, 1, 9]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results = []\n",
"\n",
"with serverless.context():\n",
" function_refs = [sleepy_function(t) for t in range(1, 10)]\n",
" \n",
" finished_refs, non_finished_refs = get_refs_by_status(function_refs)\n",
" \n",
" while len(non_finished_refs) != 0:\n",
" finished_refs, non_finished_refs = get_refs_by_status(non_finished_refs)\n",
" \n",
" results += get(finished_refs)\n",
" \n",
" print(f\"Waiting for {len(non_finished_refs)} remote functions to finish...\")\n",
" time.sleep(1)\n",
" \n",
"results"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,235 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "fda54a1b-cca5-42e8-8ce5-bb6cba55d4b8",
"metadata": {},
"source": [
"# Guide: state handling"
]
},
{
"cell_type": "markdown",
"id": "04c6a8de-56d8-47e0-9632-a2878c8bc974",
"metadata": {},
"source": [
"There are situations when you need to handle imformation flow between jobs or tasks in a workflow.\n",
"For that purposes we provide `StateHandler`s. One of the out-of-the-box handler is `RedisStateHandler`.\n",
"\n",
"Let's see an example of using state handler within job to share information."
]
},
{
"cell_type": "markdown",
"id": "17249fca-4bfc-4776-82f6-d9b44d93303b",
"metadata": {},
"source": [
"> NOTE: to run this example you must have ray head node and redis server up and running.\n",
"> You can use docker-compose to do so."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8567a5f8-f949-4145-8e7d-eb7f78108f5a",
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, Program, GatewayProvider\n",
"from quantum_serverless.core.state import RedisStateHandler"
]
},
{
"cell_type": "markdown",
"id": "a8a68b12-fe62-4b62-97df-999e5811af64",
"metadata": {},
"source": [
"Let's create serverlss object and redis handler object"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "e7a0bca1-5d05-4e2a-96b3-66f8ef723452",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gateway_provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(gateway_provider)\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "a4bfff67-be65-4b3a-80f2-d0696e3190de",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<RedisStateHandler | localhost:6379>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"state_handler = RedisStateHandler(\"localhost\", 6379)\n",
"state_handler"
]
},
{
"cell_type": "markdown",
"id": "fd083ba3-0035-4ff9-8e88-c3957cb5bc07",
"metadata": {},
"source": [
"We will be running following [job](./job_with_state.py).\n",
"\n",
"```python\n",
"# job_with_state.py\n",
"from quantum_serverless import QuantumServerless, distribute_task, get\n",
"\n",
"from quantum_serverless.core.state import RedisStateHandler\n",
"\n",
"state_handler = RedisStateHandler(\"redis\", 6379)\n",
"serverless = QuantumServerless()\n",
"\n",
"\n",
"@distribute_task(state=state_handler)\n",
"def func_with_state(state: RedisStateHandler, seed: int):\n",
" state.set(\"in_job\", {\"k\": seed})\n",
" return seed\n",
"\n",
"\n",
"@distribute_task(state=state_handler)\n",
"def other_func_with_state(state: RedisStateHandler, seed: int):\n",
" state.set(\"in_other_job\", {\"other_k\": seed})\n",
" return get(func_with_state(seed))\n",
"\n",
"\n",
"with serverless.context():\n",
" result = get(other_func_with_state(42)) # no need to pass state as it is handled in decorator\n",
"\n",
"print(result)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "bfb0a660-7354-446e-b14b-0dc97e883134",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 6>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"program = Program(\n",
" name=\"job_with_state\",\n",
" entrypoint=\"job_with_state.py\"\n",
")\n",
"\n",
"job = serverless.run_program(program)\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "6c9d41a6-cedf-4064-a1ea-fa9441c6ed9a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "markdown",
"id": "3d3a7c9e-3cd9-46b6-86c6-3281f0e7f15a",
"metadata": {},
"source": [
"As you can see from job code we saved 2 enries in our state handler `in_job` and `in_other_job`. And because we are using same handler with the same redis instance we can access stored information, by calling `get` on handler instance"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "cb0acc2d-85c8-4850-a7ee-4af1f837b3fe",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"({'k': 42}, {'other_k': 42})"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"state_handler.get(\"in_job\"), state_handler.get(\"in_other_job\")"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,15 +0,0 @@
from quantum_serverless import QuantumServerless, run_qiskit_remote, get
@run_qiskit_remote()
def ultimate():
return 42
with QuantumServerless():
result = get([
ultimate()
for _ in range(100)
])
print(result)

View File

@ -1,25 +0,0 @@
from quantum_serverless import QuantumServerless, run_qiskit_remote, get
from quantum_serverless.core.state import RedisStateHandler
state_handler = RedisStateHandler("redis", 6379)
serverless = QuantumServerless()
@run_qiskit_remote(state=state_handler)
def func_with_state(state: RedisStateHandler, seed: int):
state.set("in_job", {"k": seed})
return seed
@run_qiskit_remote(state=state_handler)
def other_func_with_state(state: RedisStateHandler, seed: int):
state.set("in_other_job", {"other_k": seed})
return get(func_with_state(seed))
with serverless:
result = get(other_func_with_state(42))
print(result)

View File

@ -1,45 +0,0 @@
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Estimator
from quantum_serverless import QuantumServerless, run_qiskit_remote, get, put, save_result
# 1. let's annotate out function to convert it
# to function that can be executed remotely
# using `run_qiskit_remote` decorator
@run_qiskit_remote()
def my_function(circuit: QuantumCircuit, obs: SparsePauliOp):
return Estimator().run([circuit], [obs]).result().values
# 2. Next let's create out serverless object to control
# where our remote function will be executed
serverless = QuantumServerless()
circuits = [random_circuit(2, 2) for _ in range(3)]
# 3. create serverless context
with serverless.context():
# 4. let's put some shared objects into remote storage that will be shared among all executions
obs_ref = put(SparsePauliOp(["ZZ"]))
# 4. run our function and get back reference to it
# as now our function it remote one
function_reference = my_function(circuits[0], obs_ref)
# 4.1 or we can run N of them in parallel (for all circuits)
function_references = [my_function(circ, obs_ref) for circ in circuits]
# 5. to get results back from reference
# we need to call `get` on function reference
print("Single execution:", get(function_reference))
print("N parallel executions:", get(function_references))
# 6. When saving results make sure objects
# passing as result are json serializable
save_result({
"single_execution": get(function_reference).tolist(),
"multiple_executions": [entry.tolist() for entry in get(function_references)]
})

BIN
docs/images/animation.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

View File

@ -2,139 +2,56 @@
Quantum serverless
##################
Quantum Serverless is a programming model for leveraging quantum and classical resources
Quantum Serverless is a programming model for leveraging quantum and classical resources.
.. image:: /images/qs_diagram.png
.. image:: /images/animation.gif
The source code to the project is available `on GitHub <https://github.com/Qiskit-Extensions/quantum-serverless>`_.
It is particularly useful for performing complex, long-running tasks on a regular basis.
------------
Here are some reasons why you might want to consider using Quantum Serverless:
**Quickstart**
`Scalability`: Quantum Serverless allows users to easily scale their jobs by running them in
parallel across multiple machines or general compute resources.
This can significantly improve performance and reduce the time it takes to
complete a job. When you hit the resource limits of your local machine,
Quantum Serverless provides horizontal scalability for both quantum and classical workloads.
Step 0: install package
.. code-block::
:caption: pip install
pip install quantum_serverless
Step 1: run infrastructure
.. code-block::
:caption: run docker compose from a root of the project
docker-compose pull
docker-compose up
Step 2: write program
.. code-block:: python
:caption: program.py
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Estimator
from quantum_serverless import QuantumServerless, run_qiskit_remote, get, put
# 1. let's annotate out function to convert it
# to function that can be executed remotely
# using `run_qiskit_remote` decorator
@run_qiskit_remote()
def my_function(circuit: QuantumCircuit, obs: SparsePauliOp):
return Estimator().run([circuit], [obs]).result().values
# 2. Next let's create out serverless object to control
# where our remote function will be executed
serverless = QuantumServerless()
circuits = [random_circuit(2, 2) for _ in range(3)]
# 3. create serverless context
with serverless.context():
# 4. let's put some shared objects into remote storage that will be shared among all executions
obs_ref = put(SparsePauliOp(["ZZ"]))
# 4. run our function and get back reference to it
# as now our function it remote one
function_reference = my_function(circuits[0], obs_ref)
# 4.1 or we can run N of them in parallel (for all circuits)
function_references = [my_function(circ, obs_ref) for circ in circuits]
# 5. to get results back from reference
# we need to call `get` on function reference
print("Single execution:", get(function_reference))
print("N parallel executions:", get(function_references))
Step 3: run program
.. code-block:: python
:caption: in jupyter notebook
from quantum_serverless import QuantumServerless, GatewayProvider, Program
provider = GatewayProvider(
username="user", # this username has already been defined in local docker setup and does not need to be changed
password="password123", # this password has already been defined in local docker setup and does not need to be changed
host="http://gateway:8000", # address of provider
)
serverless = QuantumServerless(provider)
# create out program
program = Program(
name="my_program",
entrypoint="program.py", # set entrypoint as out program.py file
working_dir="./"
)
job = serverless.run_program(program)
job.status()
# <JobStatus.SUCCEEDED: 'SUCCEEDED'>
job.logs()
# Single execution: [1.]
# N parallel executions: [array([1.]), array([0.]), array([-0.28650496])]
job.result()
# '{"status": "ok", "single": [1.0], "parallel_result": [[1.0], [0.9740035726118753], [1.0]]}'
------------
**Getting started**
`Remote execution`: Quantum Serverless also provides asynchronous remote job execution,
making it ideal for long-running scripts or jobs that you want to run somewhere and
retrieve the results later. This means that you can offload compute-intensive tasks
to a remote system and receive the results when they are ready.
.. toctree::
:maxdepth: 2
Guides <getting_started/index>
**Guides**
Quickstart <quickstart/index>
.. toctree::
:maxdepth: 2
Guides <guides/index>
**Tutorials**
Running <running/index>
.. toctree::
:maxdepth: 2
Tutorials <tutorials/index>
Development <development/index>
**API references**
.. toctree::
:maxdepth: 2
Deployment <deployment/index>
.. toctree::
:maxdepth: 1
API References <apidocs/index>
------------
The source code to the project is available `on GitHub <https://github.com/Qiskit-Extensions/quantum-serverless>`_.
.. Hiding - Indices and tables
:ref:`genindex`
:ref:`modindex`
:ref:`search`

114
docs/quickstart/index.rst Normal file
View File

@ -0,0 +1,114 @@
==========
Quickstart
==========
Step 0: install package
.. code-block::
:caption: pip install
pip install quantum_serverless==0.0.7
Step 1: run infrastructure
.. code-block::
:caption: run docker compose from a root of the project
VERSION=0.0.7 docker-compose --profile full up
Step 2: write program
.. code-block:: python
:caption: program.py
from quantum_serverless import distribute_task, get, get_arguments, save_result
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.primitives import Sampler
from qiskit.quantum_info import SparsePauliOp
# 1. Define a distributed function using the `distribute_task` decorator
@distribute_task()
def distributed_sample(circuit: QuantumCircuit):
"""Calculates quasi dists as a distributed function."""
return Sampler().run(circuit).result().quasi_dists[0]
# 2. Get the program arguments using `get_arguments`
arguments = get_arguments()
circuits = arguments.get("circuits", [])
# 3. Run the distributed function for each circuit in parallel and get execution references
function_references = [
distributed_sample(circuit)
for circuit in circuits
]
# 4. Collect all results using `get`
collected_results = get(function_references)
# 5. Save the results using `save_result`
save_result({
"quasi_dists": collected_results
})
Step 3: run program
.. code-block:: python
:caption: in jupyter notebook
from quantum_serverless import QuantumServerless, GatewayProvider
from qiskit.circuit.random import random_circuit
serverless = QuantumServerless(GatewayProvider(
username="user", # this username has already been defined in local docker setup and does not need to be changed
password="password123", # this password has already been defined in local docker setup and does not need to be changed
host="http://gateway:8000", # address of provider
))
# create program
program = Program(
title="Quickstart",
entrypoint="program.py",
working_dir="./" # or where your program file is located
)
# create inputs to our program
circuits = []
for _ in range(3):
circuit = random_circuit(3, 2)
circuit.measure_all()
circuits.append(circuit)
# run program
job = serverless.run_program(
program=program,
arguments={
"circuits": circuits
}
)
Step 4: monitor job status
.. code-block:: python
:caption: in jupyter notebook
job.status()
# <JobStatus.SUCCEEDED: 'SUCCEEDED'>
job.logs()
Step 5: get results
.. code-block:: python
:caption: in jupyter notebook
job.result()
# {"quasi_dists": [
# {"0": 0.25, "1": 0.25, "2": 0.2499999999999999, "3": 0.2499999999999999},
# {"0": 0.1512273969460124, "1": 0.0400459556274728, "6": 0.1693190975212014, "7": 0.6394075499053132},
# {"0": 0.25, "1": 0.25, "4": 0.2499999999999999, "5": 0.2499999999999999}
# ]}

10
docs/running/index.rst Normal file
View File

@ -0,0 +1,10 @@
=======
Running
=======
This section contains a series of short examples that demonstrate how to run serverless programs and showcase the main features of this package.
.. toctree::
:maxdepth: 2
Running <notebooks/index>

View File

@ -5,14 +5,17 @@
"id": "66030e20-b384-4dcf-9c5f-7664f7ad1693",
"metadata": {},
"source": [
"# Getting started - level 1\n",
"# Running a program remotely\n",
"\n",
"Let's write `Hello World` program using quantum serverless. \n",
"In this tutorial, we will write a \"Hello World\" program using Quantum Serverless. The program will be a Qiskit example that prepares a Bell state and prints the measured probability distribution.\n",
"\n",
"We will start with writing code for our program and saving it to [./source_files/gs_level_1.py](./source_files/gs_level_1.py) file. Our program will be a Qiskit hello world example, which prepares a Bell state and then returns the measured probability distribution\n",
"### Writing the Program\n",
"\n",
"First, we need to write the program code and save it to a file called [program_1.py](./source_files/program_1.py). \n",
"The code for the program is shown below:\n",
"\n",
"```python\n",
"# source_files/gs_level_1.py\n",
"# source_files/program_1.py\n",
"\n",
"from qiskit import QuantumCircuit\n",
"from qiskit.primitives import Sampler\n",
@ -30,16 +33,18 @@
"print(f\"Quasi distribution: {quasi_dists[0]}\")\n",
"```\n",
"\n",
"Next we need to run this program. For that we need to import necessary classes and configure them. \n",
"One of those classes is `QuantumServerless`, which is a client class to interact with compute resources.\n",
"It will help us run programs, monitor progress and fetch results.\n",
"This program creates a two-qubit quantum circuit that prepares a Bell state, measures the result, and prints the measured probability distribution.\n",
"\n",
"`QuantumServerless` accepts `Provider` as a constructor argument. Provider stores configuration where our compute resources are and how to connect to them. For this example we will be using provider which is connected to local docker-compose setup. For more information on docker-compose check out [docker docs](https://docs.docker.com/compose/), but for now you can think of it as your local environment manager. So, in this example programs will be running locally on your machine. If you want to run it elsewhere, you need to provide corresponding host and authentication details."
"### Running the Program\n",
"\n",
"To run the program, we need to import the necessary classes and configure them. One of these classes is QuantumServerless, which is a client class for interacting with compute resources.\n",
"\n",
"QuantumServerless takes a Provider object as a constructor argument. The Provider object stores configuration information about our compute resources, such as where they are located and how to connect to them. In this example, we will use a provider that is connected to a local Docker Compose setup. In this case, it allows us to run the program locally on our machine. If you want to run the program elsewhere, you will need to provide the corresponding host and authentication details."
]
},
{
"cell_type": "code",
"execution_count": 43,
"execution_count": 1,
"id": "81dd7807-7180-4b87-bbf9-832b7cf29d69",
"metadata": {},
"outputs": [],
@ -49,7 +54,7 @@
},
{
"cell_type": "code",
"execution_count": 68,
"execution_count": 3,
"id": "acdec789-4967-48ee-8f6c-8d2b0ff57e91",
"metadata": {},
"outputs": [
@ -59,16 +64,16 @@
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 68,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\", # this username has already been defined in local docker setup and does not need to be changed\n",
" password=\"password123\", # this password has already been defined in local docker setup and does not need to be changed\n",
" host=\"http://gateway:8000\", # address of provider\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
@ -80,7 +85,7 @@
"id": "4dd85621-9ab0-4f34-9ab4-07ad773c5e00",
"metadata": {},
"source": [
"Now we need to run our program file, by creating an instance of `Program` and calling `run_program` method of our `QuantumServerless` client.\n",
"After importing the necessary classes and configuring them, we can run the program by calling the `run_program()` method of the `QuantumServerless` object:\n",
"\n",
"`Program` accepts couple of required parameters:\n",
"- title - name of the program\n",
@ -90,17 +95,17 @@
},
{
"cell_type": "code",
"execution_count": 82,
"execution_count": 4,
"id": "d51df836-3f22-467c-b637-5803145d5d8a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 72c73473-543a-436f-8920-251709ebc829>"
"<Job | abff8a68-39d5-4e0b-8e3e-8b55e63dcd9e>"
]
},
"execution_count": 82,
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
@ -109,9 +114,9 @@
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"Getting started program level 1\", # you can choose any name you like. It is used to differentiate if you have a lot of programs in array.\n",
" entrypoint=\"gs_level_1.py\", # entrypoint is file that will start your calculation\n",
" working_dir=\"./source_files/\" # where you files are located. By default it is current directory.\n",
" title=\"First program\",\n",
" entrypoint=\"program_1.py\",\n",
" working_dir=\"./source_files/\"\n",
")\n",
"\n",
"job = serverless.run_program(program)\n",
@ -128,7 +133,7 @@
},
{
"cell_type": "code",
"execution_count": 85,
"execution_count": 5,
"id": "cc7ccea6-bbae-4184-ba7f-67b6c20a0b0b",
"metadata": {},
"outputs": [
@ -138,7 +143,7 @@
"'SUCCEEDED'"
]
},
"execution_count": 85,
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@ -149,7 +154,7 @@
},
{
"cell_type": "code",
"execution_count": 86,
"execution_count": 6,
"id": "ca76abfa-2ff5-425b-a225-058d91348e8b",
"metadata": {},
"outputs": [
@ -159,7 +164,7 @@
"'Quasi distribution: {0: 0.4999999999999999, 3: 0.4999999999999999}\\n'"
]
},
"execution_count": 86,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@ -179,17 +184,17 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 7,
"id": "f942b76d-596c-4384-8f36-e5f73e72cefd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'aae3533f-ed0d-4e93-9247-4705d476ee97'"
"'abff8a68-39d5-4e0b-8e3e-8b55e63dcd9e'"
]
},
"execution_count": 20,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@ -208,17 +213,17 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 8,
"id": "45e2927f-655b-47a4-8003-f16e5ba0a1cd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | aae3533f-ed0d-4e93-9247-4705d476ee97>"
"<Job | abff8a68-39d5-4e0b-8e3e-8b55e63dcd9e>"
]
},
"execution_count": 21,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@ -226,6 +231,35 @@
"source": [
"serverless.get_job_by_id(job.job_id)"
]
},
{
"cell_type": "markdown",
"id": "e14885fd-8b9c-4d61-a192-b19ba400567f",
"metadata": {},
"source": [
"To get a list of previously ran jobs client has `get_jobs` method."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "e9a2c091-dcf9-4b74-9348-e8a76966fc39",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<Job | abff8a68-39d5-4e0b-8e3e-8b55e63dcd9e>]"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"serverless.get_jobs()"
]
}
],
"metadata": {

View File

@ -0,0 +1,240 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Parallel workflows in programs\n",
"\n",
"In this document, we will learn how to run distributed workflows inside a program.\n",
"\n",
"Let's take a look at the program file [./source_files/program_4.py](./source_files/program_4.py). \n",
"\n",
"```python\n",
"# source_files/program_4.py\n",
"\n",
"from quantum_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 program, so let's go over them in more detail:\n",
"\n",
"The `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 program.\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 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 program 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 program arguments using the `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` function.\n",
"\n",
"Essentially, this program reads the circuits from the program arguments, executes the distributed_sample function on each circuit in parallel, collects the results from the function executions, and saves the results."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create list of random circuit which we will be using as arguments"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdac1c5e850>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdab1a5fb50>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdac1c5e8d0>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdac1c5ea90>,\n",
" <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fdac1c5ec10>]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from qiskit.circuit.random import random_circuit\n",
"\n",
"circuits = [random_circuit(2, 2) for _ in range(5)]\n",
"[circuit.measure_all() for circuit in circuits]\n",
"circuits"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run program as usual"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 6bd54632-bce0-4214-a5e1-04f931ab7dcb>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"Program with distributed workflow\",\n",
" entrypoint=\"program_4.py\",\n",
" working_dir=\"./source_files/\"\n",
")\n",
"\n",
"job = serverless.run_program(program, arguments={\"circuits\": circuits})\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"results\": [{\"0\": 0.0019388099833208, \"1\": 0.0246690007437, \"2\": 0.0709273872101196, \"3\": 0.9024648020628592}, {\"0\": 0.4999999999999999, \"2\": 0.4999999999999999}, {\"0\": 0.0008967561725936, \"1\": 2.42854928976e-05, \"3\": 0.9990789583345085}, {\"0\": 0.0769441987062216, \"1\": 0.0769441987062216, \"2\": 0.4230558012937782, \"3\": 0.4230558012937782}, {\"0\": 0.1019745637494164, \"1\": 0.2181326517505121, \"2\": 0.216589213671712, \"3\": 0.4633035708283594}]}'"
]
},
"execution_count": 20,
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,216 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Programs with arguments and saved results\n",
"\n",
"In this document, we will learn how to pass arguments to our program and save results.\n",
"\n",
"Let's create another file with our program [./source_files/program_2.py](./source_files/program_2.py). \n",
"\n",
"Instead of having the circuit defined inside the program (like we did in first example), we will pass it as an argument. We will also save the results, so we can access them later by calling `save_result()`.\n",
"\n",
"Here is the program:\n",
"\n",
"```python\n",
"# source_files/program_2.py\n",
"\n",
"from quantum_serverless import get_arguments, save_result\n",
"\n",
"from qiskit.primitives import Sampler\n",
"\n",
"# get all arguments passed to this program\n",
"arguments = get_arguments()\n",
"\n",
"# get specific argument that we are interested in\n",
"circuit = arguments.get(\"circuit\")\n",
"\n",
"sampler = Sampler()\n",
"\n",
"quasi_dists = sampler.run(circuit).result().quasi_dists\n",
"\n",
"print(f\"Quasi distribution: {quasi_dists[0]}\")\n",
"\n",
"# saving results of a program\n",
"save_result({\n",
" \"quasi_dists\": quasi_dists[0]\n",
"})\n",
"```\n",
"\n",
"As you can see, the circuit construction is not inside the program anymore. Instead, we parse the arguments by calling the `get_arguments()` function.\n",
"\n",
"Another change is the call to the `save_result()` function, which saves the results of the execution as serialized JSON.\n",
"\n",
"Let's create and configure out client"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we will create circuit that we want to pass as an argument to the program."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\"> ┌───┐ ░ ┌─┐ \n",
" q_0: ┤ H ├──■───░─┤M├───\n",
" └───┘┌─┴─┐ ░ └╥┘┌─┐\n",
" q_1: ─────┤ X ├─░──╫─┤M├\n",
" └───┘ ░ ║ └╥┘\n",
"meas: 2/══════════════╩══╩═\n",
" 0 1 </pre>"
],
"text/plain": [
" ┌───┐ ░ ┌─┐ \n",
" q_0: ┤ H ├──■───░─┤M├───\n",
" └───┘┌─┴─┐ ░ └╥┘┌─┐\n",
" q_1: ─────┤ X ├─░──╫─┤M├\n",
" └───┘ ░ ║ └╥┘\n",
"meas: 2/══════════════╩══╩═\n",
" 0 1 "
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from qiskit import QuantumCircuit\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": [
"And finally we are creating our program and running it using `run_program` method and passing `arguments` parameter in form of dictionary with `circuit` key and our circuit as value"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 6a316e4d-6860-4367-9eb4-82640b6f854b>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"Program with arguments\",\n",
" entrypoint=\"program_3.py\",\n",
" working_dir=\"./source_files/\"\n",
")\n",
"\n",
"job = serverless.run_program(program, arguments={\"circuit\": circuit})\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"quasi_dists\": {\"0\": 0.4999999999999999, \"3\": 0.4999999999999999}}'"
]
},
"execution_count": 13,
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,201 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"# Dependency management for programs\n",
"\n",
"In this document, we will learn how to install custom dependencies to your program.\n",
"\n",
"Let's create another file with our new program [./source_files/program_3.py](./source_files/program_3.py). \n",
"\n",
"For the sake of this example, let's use `qiskit-experiments` as our custom dependency. We will use randomized benchmarking (RB) circuits from qiskit-experiments, composed with the circuit from the input arguments for measurement.\n",
"\n",
"Here's what the file would look like:\n",
"\n",
"```python\n",
"# source_files/program_3.py\n",
"\n",
"from quantum_serverless import get_arguments, save_result\n",
"\n",
"from qiskit.primitives import Sampler\n",
"from qiskit_experiments.library import StandardRB\n",
"\n",
"\n",
"arguments = get_arguments()\n",
"\n",
"circuit = arguments.get(\"circuit\")\n",
"\n",
"rb = StandardRB(\n",
" physical_qubits=(1,),\n",
" lengths=list(range(1, 300, 30)),\n",
" seed=42\n",
")\n",
"composed = circuit.compose(rb.circuits()[0])\n",
"\n",
"sampler = Sampler()\n",
"\n",
"quasi_dists = sampler.run(composed).result().quasi_dists\n",
"\n",
"print(f\"Quasi distribution: {quasi_dists[0]}\")\n",
"\n",
"# saving results of a program\n",
"save_result({\n",
" \"quasi_dists\": quasi_dists[0]\n",
"})\n",
"\n",
"```\n",
"\n",
"As you can see, we've imported our custom dependency `qiskit-experiments` and used its `StandardRB` module to generate an RB circuit, which we've composed with the circuit provided in the input arguments.\n",
"\n",
"Let's create and configure out client"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"from quantum_serverless import QuantumServerless, GatewayProvider"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [gateway-provider]>"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://localhost:8000\",\n",
")\n",
"\n",
"serverless = QuantumServerless(provider)\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": [
"from qiskit.circuit.random import random_circuit\n",
"circuit = random_circuit(2, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To install custom dependency that our program might use we need to pass it as `dependencies` constructor argument for `Program` class. \n",
"You can pass multiple dependencies and specify versions. "
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | cd17e4d8-5109-4c88-ab98-c5feadabb277>"
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program\n",
"\n",
"program = Program(\n",
" title=\"Program with dependencies\",\n",
" entrypoint=\"program_3.py\",\n",
" working_dir=\"./source_files/\",\n",
" dependencies=[\"qiskit-experiments\"]\n",
")\n",
"\n",
"job = serverless.run_program(program, arguments={\"circuit\": circuit})\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 78,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 78,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Quasi distribution: {0: 1.0}\n",
"\n"
]
}
],
"source": [
"print(job.logs())"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,22 @@
# source_files/program_2.py
from quantum_serverless import get_arguments, save_result
from qiskit.primitives import Sampler
# get all arguments passed to this program
arguments = get_arguments()
# get specific argument that we are interested in
circuit = arguments.get("circuit")
sampler = Sampler()
quasi_dists = sampler.run(circuit).result().quasi_dists
print(f"Quasi distribution: {quasi_dists[0]}")
# saving results of a program
save_result({
"quasi_dists": quasi_dists[0]
})

View File

@ -0,0 +1,29 @@
# source_files/program_3.py
from quantum_serverless import get_arguments, save_result
from qiskit.primitives import Sampler
from qiskit_experiments.library import StandardRB
arguments = get_arguments()
circuit = arguments.get("circuit")
rb = StandardRB(
physical_qubits=(1,),
lengths=list(range(1, 300, 30)),
seed=42
)
composed = circuit.compose(rb.circuits()[0])
sampler = Sampler()
quasi_dists = sampler.run(composed).result().quasi_dists
print(f"Quasi distribution: {quasi_dists[0]}")
# saving results of a program
save_result({
"quasi_dists": quasi_dists[0]
})

View File

@ -0,0 +1,31 @@
# source_files/program_4.py
from quantum_serverless import get_arguments, save_result, distribute_task, get
from qiskit import QuantumCircuit
from qiskit.primitives import Sampler
@distribute_task()
def distributed_sample(circuit: QuantumCircuit):
"""Distributed task that returns quasi distribution for given circuit."""
return Sampler().run(circuit).result().quasi_dists[0]
arguments = get_arguments()
circuits = arguments.get("circuits")
# run distributed tasks as async function
# we get task references as a return type
sample_task_references = [
distributed_sample(circuit)
for circuit in circuits
]
# now we need to collect results from task references
results = get(sample_task_references)
save_result({
"results": results
})

View File

@ -1,408 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "978d83ac-86ca-4fb3-8b43-81c02c7be120",
"metadata": {},
"source": [
"# Tutorial: Overview of Quantum Serverless\n",
"\n",
"Structure:\n",
"\n",
"- background, motivation and goals\n",
"- building blocks of distributed computation\n",
"- resource allocation and execution management\n",
"- assembling all together\n",
"- running as async program"
]
},
{
"cell_type": "markdown",
"id": "0d5fbcfb-f32d-4203-98aa-ceac59924351",
"metadata": {},
"source": [
"## Background, motivation and goals\n",
"\n",
"**Background and motivation**\n",
"\n",
"Today a lot of experiments are happening on a local machines or on manually allocated remote resources. This approach is not scalable and we need to give users a tool to run quantum-classical workloads without worrying about allocating and managing underlying infrastructure.\n",
"\n",
"To achieve the quantum advantage we need to figure out mechanism to combine Classical + Quantum computation, and how orchestrate it.\n",
"\n",
"\n",
"**Goals**\n",
"\n",
"Create a set of tools that allow user to execute hybrid workloads (combine/orchestrate Classical and Quantum computation).\n",
"\n",
"Efficiently manage abstractions for Quantum and Classical workloads.\n",
"\n",
"Maximizes flexibility for users in multi-cloud and HPC scenarios."
]
},
{
"cell_type": "markdown",
"id": "dc56c508-a0be-403f-9825-767e8a24e286",
"metadata": {},
"source": [
"## Building blocks of distributed computation"
]
},
{
"cell_type": "markdown",
"id": "047459e0-7a9e-4597-9f1b-af25dbadffb9",
"metadata": {},
"source": [
"**Compute resources**\n",
"\n",
"Compute resources can be described as set of computational nodes with resources associated to each of them.\n",
"Nodes are performing computation itself. \n",
"Distributed storage is for accessing sharable resources between nodes.\n",
"\n",
"![compute resources](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/tutorials/images/diagrams_compute_resource.png)\n",
"\n",
"\n",
"In order to resolve distributed compute we need a way to orchestrate our workloads on remote resources.\n",
"\n",
"**Object**\n",
"\n",
"Let's first look how to turn a local object into a remote one.\n",
"\n",
"To turn your local object to distributed one you need to call `put` function. \n",
"It will serialize your object and send it to distributed storage and return you a reference to remote object location. \n",
"\n",
"![objects diagram](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/tutorials/images/diagrams_put.png)\n",
"\n",
"Example\n",
"\n",
"```python\n",
"cirucit = QuantumCircuit(N, M)\n",
"...\n",
"circuit_reference = quantum_serverless.put(circuit)\n",
"```\n",
"\n",
"Now we know how to move local object to remote compute resource. \n",
"Next we need to do something with it. For this we will have remote functions.\n",
"\n",
"**Functions**\n",
"\n",
"As a second step we need to know how to transform our local function into remote one. \n",
"\n",
"In order to do that you need to annotate function with `distribute_task` decorator. \n",
"This will turn your function into remote executable. \n",
"\n",
"![functions diagram](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/tutorials/images/diagrams_function.png)\n",
"\n",
"All you need to do now is call this function and it will be executed as a remote procedure automatically. \n",
"\n",
"If you run N instances of this function in parallel you need just call it N times in a loop. \n",
"The result of the following code will be a list of references to execution results.\n",
"\n",
"Example\n",
"\n",
"```python\n",
"@quantum_serverless.distribute_task()\n",
"def exp_val_remote(cirucit, obs):\n",
" estimator = Estimator(...)\n",
" return estimator.run(circuit, obs)\n",
"\n",
"obs: Operator = ...\n",
"\n",
"exp_val_execution_reference = exp_val_remote(circuit, obs)\n",
"\n",
"circuit_list: List[QuantumCircuit] = [...]\n",
"\n",
"obs_ref = quantum_serverless.put(obs)\n",
"\n",
"exp_val_execution_references = [\n",
" exp_val_remote(circ, obs_ref) \n",
" for circ in circuit_list\n",
"]\n",
"```\n",
"\n",
"As you can see we are passing an observable by refernce and circuits by values. Attributes passed by value will be converted to remote objects automatically and resolved within the remote function. We are passing observable by referece because we know it is the same one shared by all parallel function invocations, so we are saving some space. \n",
"\n",
"**Collecting results**\n",
"\n",
"And of course you need to collect your results back from remote resources. \n",
"In order to do that you need to call the `get` function, which can be applied to object and function references or, alternatively, to a list of references.\n",
"\n",
"![collecting results diagram](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/tutorials/images/diagram_get.png)\n",
"\n",
"Example\n",
"\n",
"```python\n",
"collected_circuit = quantum_serverless.get(circuit_reference)\n",
"# <QuantumCircuit ...>\n",
"\n",
"collected_exp_value = quantum_serverless.get(exp_val_execution_reference)\n",
"# <EstimatorResult ...>\n",
"collected_exp_values = quantum_serverless.get(exp_val_execution_references)\n",
"# [<EstimatorResult ...>, <EstimatorResult ...>, ...]\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "9b092b14-f6f5-43f8-a750-7c7ccc13378b",
"metadata": {},
"source": [
"## Resource allocation and execution management"
]
},
{
"cell_type": "markdown",
"id": "31dd73bf-2d32-4568-a7aa-bb6883488bcc",
"metadata": {},
"source": [
"**Resource allocation**\n",
"\n",
"Some functions are more demanding than others. \n",
"To allocate specific resources to a function, the decorator accepts `target` parameter.\n",
"For example, your function may require 2 cpus to be executed. You can pass `target={'cpu': 2}` to decorator and this function will request 2 cpus from your resource capacity to be executed.\n",
"\n",
"![resource allocation diagram](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/tutorials/images/diagrams_resource_allocation.png)\n",
"\n",
"Example\n",
"\n",
"```python\n",
"@quantum_serverless.distribute_task(target={\"cpu\": 2, \"mem\": 8})\n",
"def exp_val_remote(cirucit, obs):\n",
" estimator = Estimator(...)\n",
" exp_val_result = estimator.run(circuit, obs)\n",
" ...\n",
" return result\n",
"```\n",
"\n",
"**Execution management**\n",
"\n",
"Now we figured how to convert our function and objects into remote counterparts and how to request for specific resources.\n",
"Next step would be deciding where to run your workloads. \n",
"\n",
"To have control over location of compute we use configurations in `QuantumServerless` object and python context managers. \n",
"\n",
"`QuantumServerless` has a notion of `providers` which are abstractions to define where your compute resources are located.\n",
"\n",
"By default you always have a `local` provider which is your local machine where nodes and local cores.\n",
"And you can configure as many providers as you want.\n",
"\n",
"![execution management diagram](https://raw.githubusercontent.com/Qiskit-Extensions/quantum-serverless/main/docs/tutorials/images/diagrams_context_management.png)\n",
"\n",
"Example\n",
"\n",
"```python\n",
"serverless = QuantumServerless({\"providers\": [...]})\n",
"serverless = QuantumServerless.load_configuration(\"<PATH_TO_CONFIG_FILE>\")\n",
"print(serverless)\n",
"# <QuantumServerless: providers [local, ibm-cloud, ...]>\n",
"\n",
"with serverless.context(\"local\"):\n",
" exp_val_execution_reference = exp_val_remote(circuit, obs)\n",
" collected_exp_value = quantum_serverless.get(exp_val_execution_reference)\n",
"\n",
"with serverless.context(\"ibm-cloud\"):\n",
" exp_val_execution_reference = exp_val_remote(circuit, obs)\n",
" collected_exp_value = quantum_serverless.get(exp_val_execution_reference)\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "3a8c5fc8-5e85-4d47-aa9f-1e3b849783a4",
"metadata": {},
"source": [
"## Assembling all together\n",
"\n",
"Now we have all the important concepts in place and we can assemble our first simple example of a distributed program.\n",
"\n",
"> NOTE: make sure to run docker-compose to use this example"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6277cbd3-a1ac-4a8f-8a29-c9c92732e780",
"metadata": {},
"outputs": [],
"source": [
"from qiskit.circuit.random import random_circuit\n",
"from qiskit.primitives import Estimator\n",
"from qiskit.quantum_info import SparsePauliOp\n",
"from quantum_serverless import QuantumServerless, distribute_task, put, get\n",
"\n",
"serverless = QuantumServerless()\n",
"\n",
"@distribute_task()\n",
"def exp_val_remote(circuit, obs):\n",
" estimator = Estimator()\n",
" return estimator.run(circuit, obs).result()\n",
"\n",
"circuit_list = [random_circuit(2, 2) for _ in range(3)]\n",
"obs = SparsePauliOp([\"IZ\"])\n",
"\n",
"with serverless.context():\n",
" obs_ref = put(obs)\n",
" exp_val_execution_references = [\n",
" exp_val_remote(circ, obs_ref) \n",
" for circ in circuit_list\n",
" ]\n",
" collected_exp_values = get(exp_val_execution_references)\n",
" \n",
"print(collected_exp_values)"
]
},
{
"cell_type": "markdown",
"id": "a962b872-427d-47c6-9eea-56a53bdfcdc6",
"metadata": {},
"source": [
"## Running as async program\n",
"\n",
"In most of the cases we want to run our scripts as sync programs, so we can lunch them and forget, then later on check results.\n",
"In order to do so we will use `Program` interface.\n",
"\n",
"Let's reuse benchmark script which covers all the concepts covered above and which you can find [here](./source_files/benchmark.py)."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "75efe890-d7ea-4747-973c-b90e4df9fe39",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Job | 14>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from quantum_serverless import Program, GatewayProvider\n",
"\n",
"gateway_provider = GatewayProvider(\n",
" username=\"user\",\n",
" password=\"password123\",\n",
" host=\"http://gateway:8000\",\n",
")\n",
"\n",
"program = Program(\n",
" title=\"brnchmark_program\",\n",
" entrypoint=\"benchmark.py\",\n",
" working_dir=\"./source_files/\",\n",
" description=\"Benchmark program\"\n",
")\n",
"\n",
"job = serverless.set_provider(gateway_provider).run_program(program)\n",
"job"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "0a7c52b4-c51e-4497-a013-5b1fdc9a5a79",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'SUCCEEDED'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job.status()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "f650750b-4b39-4932-b9dc-a51171d1d146",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Execution time: 9.168829679489136\n",
"Results: [[EstimatorResult(values=array([-4.04804878e-01+6.73804871e-01j, -1.94289029e-16-8.76563346e-01j,\n",
" 0.00000000e+00+2.93954499e-16j, 0.00000000e+00+1.00000000e+00j,\n",
" -5.78736907e-01-2.02739106e-01j, -8.32667268e-17-1.00000000e+00j,\n",
" -4.89359541e-16-1.15784406e-16j, 0.00000000e+00+3.87904546e-16j,\n",
" -5.34438212e-01+5.55111512e-17j, -3.51585782e-17+1.06106206e+00j,\n",
" -2.49262374e-02-2.61221914e-16j, 1.00000000e+00+1.00000000e+00j,\n",
" -4.49088083e-01+0.00000000e+00j, -1.00000000e+00+5.73423770e-17j,\n",
" -1.00000000e+00+4.90611306e-01j, -3.14018492e-16+9.81307787e-17j,\n",
" 1.81756717e-01-3.15451888e-02j, -1.33434861e+00-1.11022302e-16j,\n",
" 6.02950130e-02+1.56597418e-02j, -4.34512241e-01-3.34194313e-01j,\n",
" 0.00000000e+00+1.00000000e+00j, 0.00000000e+00-1.00000000e+00j,\n",
" 9.56512057e-01-2.30245304e-01j, -6.87854373e-01-7.04747198e-01j,\n",
" -8.67447001e-01-2.58061563e-16j, -1.74208517e-16+1.69868804e-16j,\n",
" -8.30268506e-01+0.00000000e+00j, 5.28163400e-01-5.55111512e-17j,\n",
" 9.06271517e-01-1.00000000e+00j, 1.69275103e-01+8.24470603e-01j]), metadata=[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]), EstimatorResult(values=array([-4.04804878e-01+6.73804871e-01j, -1.94289029e-16-8.76563346e-01j,\n",
" 0.00000000e+00+2.93954499e-16j, 0.00000000e+00+1.00000000e+00j,\n",
" -5.78736907e-01-2.02739106e-01j, -8.32667268e-17-1.00000000e+00j,\n",
" -4.89359541e-16-1.15784406e-16j, 0.00000000e+00+3.87904546e-16j,\n",
" -5.34438212e-01+5.55111512e-17j, -3.51585782e-17+1.06106206e+00j,\n",
" -2.49262374e-02-2.61221914e-16j, 1.00000000e+00+1.00000000e+00j,\n",
" -4.49088083e-01+0.00000000e+00j, -1.00000000e+00+5.73423770e-17j,\n",
" -1.00000000e+00+4.90611306e-01j, -3.14018492e-16+9.81307787e-17j,\n",
" 1.81756717e-01-3.15451888e-02j, -1.33434861e+00-1.11022302e-16j,\n",
" 6.02950130e-02+1.56597418e-02j, -4.34512241e-01-3.34194313e-01j,\n",
" 0.00000000e+00+1.00000000e+00j, 0.00000000e+00-1.00000000e+00j,\n",
" 9.56512057e-01-2.30245304e-01j, -6.87854373e-01-7.04747198e-01j,\n",
" -8.67447001e-01-2.58061563e-16j, -1.74208517e-16+1.69868804e-16j,\n",
" -8.30268506e-01+0.00000000e+00j, 5.28163400e-01-5.55111512e-17j,\n",
" 9.06271517e-01-1.00000000e+00j, 1.69275103e-01+8.24470603e-01j]), metadata=[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]), EstimatorResult(values=array([-4.04804878e-01+6.73804871e-01j, -1.94289029e-16-8.76563346e-01j,\n",
" 0.00000000e+00+2.93954499e-16j, 0.00000000e+00+1.00000000e+00j,\n",
" -5.78736907e-01-2.02739106e-01j, -8.32667268e-17-1.00000000e+00j,\n",
" -4.89359541e-16-1.15784406e-16j, 0.00000000e+00+3.87904546e-16j,\n",
" -5.34438212e-01+5.55111512e-17j, -3.51585782e-17+1.06106206e+00j,\n",
" -2.49262374e-02-2.61221914e-16j, 1.00000000e+00+1.00000000e+00j,\n",
" -4.49088083e-01+0.00000000e+00j, -1.00000000e+00+5.73423770e-17j,\n",
" -1.00000000e+00+4.90611306e-01j, -3.14018492e-16+9.81307787e-17j,\n",
" 1.81756717e-01-3.15451888e-02j, -1.33434861e+00-1.11022302e-16j,\n",
" 6.02950130e-02+1.56597418e-02j, -4.34512241e-01-3.34194313e-01j,\n",
" 0.00000000e+00+1.00000000e+00j, 0.00000000e+00-1.00000000e+00j,\n",
" 9.56512057e-01-2.30245304e-01j, -6.87854373e-01-7.04747198e-01j,\n",
" -8.67447001e-01-2.58061563e-16j, -1.74208517e-16+1.69868804e-16j,\n",
" -8.30268506e-01+0.00000000e+00j, 5.28163400e-01-5.55111512e-17j,\n",
" 9.06271517e-01-1.00000000e+00j, 1.69275103e-01+8.24470603e-01j]), metadata=[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}])]]\n",
"\n"
]
}
],
"source": [
"print(job.logs())"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,270 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "43e30efc-7530-4d39-8a6f-1fb20e3d0168",
"metadata": {},
"source": [
"# Tutorial: approaches to serverless code writing"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "cbbc03aa-2ef1-49b1-8451-b4c2916fa424",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import warnings\n",
"\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "977c8d25-e652-4cc0-81c4-c2c9e8d8e9c9",
"metadata": {},
"outputs": [],
"source": [
"from typing import List, Union, Optional, Dict\n",
"\n",
"from qiskit import QuantumCircuit, transpile\n",
"from qiskit.providers import Backend\n",
"from qiskit.circuit.random import random_circuit\n",
"from qiskit.providers.aer import AerSimulator\n",
"from qiskit.providers.fake_provider import FakeVigo, FakeAlmaden, FakeBrooklyn, FakeCasablanca\n",
"\n",
"from quantum_serverless import QuantumServerless, distribute_task, get, put"
]
},
{
"cell_type": "markdown",
"id": "44df5050-294b-44cb-a826-032fe09a9d7f",
"metadata": {},
"source": [
"---\n",
"\n",
"There are multiple approaches to write serverless code using ray.\n",
"\n",
"We will review 2 of them:\n",
"\n",
"- exposing all details to user\n",
"- hiding details from user"
]
},
{
"cell_type": "markdown",
"id": "22997039-ab24-4315-a072-e0ad66d4068f",
"metadata": {},
"source": [
"In our examples we will be building functions and classes that can transpile circuits remotely and in parallel manner"
]
},
{
"cell_type": "markdown",
"id": "0044606f-4e0c-43f4-a5b5-bd7cc96f4df7",
"metadata": {},
"source": [
"### Approach #1: revealing details of implementations to user\n",
"\n",
"One way of writing your modules, classes and functions in a way that user himself handling ray stuff."
]
},
{
"cell_type": "markdown",
"id": "6958b634-a48c-44e0-9497-ad8d6b389326",
"metadata": {},
"source": [
"Inside your module you will have something like that"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ee2cec7b-b41b-4ad9-bcb7-eb7dc0a73ea8",
"metadata": {},
"outputs": [],
"source": [
"# ==================\n",
"# Insude your module\n",
"# ==================\n",
"\n",
"# your_module.transpiler\n",
"\n",
"@distribute_task()\n",
"def remote_transpile(circuits: List[QuantumCircuit], backend: Backend):\n",
" return transpile(circuits=circuits, backend=backend) "
]
},
{
"cell_type": "markdown",
"id": "84396aaa-8812-4a5a-9bd6-371dc0ac3291",
"metadata": {},
"source": [
"Then the user will use this funciton in following way"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10a3945b-05ca-4a9e-b7e2-0f872c5385be",
"metadata": {},
"outputs": [],
"source": [
"# ================\n",
"# User perspective\n",
"# ================\n",
"\n",
"# from your_module.transpiler import remote_transpile\n",
"\n",
"serverless = QuantumServerless()\n",
"\n",
"with serverless.context():\n",
" # First we need to put our object to cluster and pass object ids to functions\n",
"\n",
" # Let's do that for circuits\n",
" circuits = [random_circuit(3, 2) for _ in range(10)]\n",
" circuits_id = put(circuits)\n",
"\n",
" # and for backends\n",
" backend_ids = []\n",
" for fake_backend in [FakeAlmaden(), FakeBrooklyn(), FakeCasablanca(), FakeVigo()]:\n",
" backend = AerSimulator.from_backend(fake_backend)\n",
" backend_id = put(backend)\n",
" backend_ids.append(backend_id)\n",
" \n",
" # now we need to call remote function and pass all objects to form tasks\n",
" tasks = []\n",
" for backend_id in backend_ids:\n",
" task_id = remote_transpile(circuits_id, backend_id)\n",
" tasks.append(task_id)\n",
" \n",
" print(f\"Results: {get(tasks)}\")"
]
},
{
"cell_type": "markdown",
"id": "267d6929-730f-4445-822d-eb4c245fe4a4",
"metadata": {},
"source": [
"---\n",
"\n",
"As you can see, the user has to create a lot of boilerplate code and to pass ray objects and pointers across the application.\n",
"\n",
"### Approach #2: hiding details of implementations from user\n",
"\n",
"\n",
"We can do better by hiding all of these details inside our module. Let's have a look."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "32e0770c-91f8-451a-88e8-af495349c18e",
"metadata": {},
"outputs": [],
"source": [
"# ==================\n",
"# Insude your module\n",
"# ==================\n",
"\n",
"# your_module.transpiler\n",
"\n",
"@distribute_task()\n",
"def remote_transpile(circuits: List[QuantumCircuit], backend: Backend):\n",
" return transpile(circuits=circuits, backend=backend) \n",
"\n",
"\n",
"def parallel_transpile(circuits: List[QuantumCircuit], backends: List[Backend]) -> List[QuantumCircuit]:\n",
" circuits = [random_circuit(3, 2) for _ in range(10)]\n",
" circuits_id = put(circuits)\n",
"\n",
" backend_ids = []\n",
" for backend in backends:\n",
" backend_id = put(backend)\n",
" backend_ids.append(backend_id)\n",
" \n",
" # now we need to call remote function and pass all objects to form tasks\n",
" tasks = []\n",
" for backend_id in backend_ids:\n",
" task_id = remote_transpile(circuits_id, backend_id)\n",
" tasks.append(task_id)\n",
" \n",
" return get(tasks)"
]
},
{
"cell_type": "markdown",
"id": "c1366e65-110d-4ad9-9697-f831e6dc33e1",
"metadata": {},
"source": [
"Now let's see how code shrinks from the user's perspective, which may, in certain situations, provide a better user experience."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "72494d42-5c5e-4249-9f30-9bc45288b253",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Results: [[<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0ca0d90>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1b0c8bd90>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1814c9790>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc181484190>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0d243d0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190b5d110>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190ee9510>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1b0ced110>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1b0ced590>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1814e1150>], [<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1b0c8bcd0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1814e1c10>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0c89150>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0c89ed0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc18137fb10>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1b0df7a10>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1d15fbd90>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0bfef50>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0bfe850>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1a19e7b10>], [<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc18137f050>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190eb5290>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190ad8e10>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190aef990>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190b92390>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc181369c50>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0b904d0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190aa4250>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1814e1050>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc181484310>], [<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0cd9250>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc181369790>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190bdc4d0>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc181494f10>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc190f01150>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1d1612e90>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1e0d0c910>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1b0d0d410>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1a1a13610>, <qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7fc1a1b28510>]]\n"
]
}
],
"source": [
"# ================\n",
"# User perspective\n",
"# ================\n",
"\n",
"# from your_module.transpiler import remote_transpile\n",
"\n",
"circuits = [random_circuit(3, 2) for _ in range(10)]\n",
"backends = [AerSimulator.from_backend(backend)\n",
" for backend in [FakeAlmaden(), FakeBrooklyn(), FakeCasablanca(), FakeVigo()]]\n",
"\n",
"serverless = QuantumServerless()\n",
"\n",
"with serverless.context():\n",
" print(f\"Results: {parallel_transpile(circuits, backends)}\")"
]
},
{
"cell_type": "markdown",
"id": "1618b751-1f3c-464c-85a2-c76d790c8f53",
"metadata": {},
"source": [
"---\n",
"\n",
"Now we have only 2 lines of code to call our serverless library.\n",
"It hides all unnecessary details from the user."
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,132 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "299e7d6e-aa80-446f-b5f5-b079cb7ba769",
"metadata": {},
"source": [
"# Tutorial: writing serverless package from scratch"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e2d281fa-11ae-4e29-a995-9cfc9abd8e65",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import warnings\n",
"\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "markdown",
"id": "162430b8-f026-4dd0-b00f-28ba90aa791f",
"metadata": {},
"source": [
"---\n",
"\n",
"There are couple of simple rules that you need to follow in order to write software packages with serverless.\n",
"\n",
"- Hide all unnecessary details from user to improve user experience\n",
" - hide ray library calls\n",
" - user should not be aware of ray if he do not need to\n",
"- request as little resources as possible, it is better to have 10 function calls which require 1 cpu, than 1 function call which requires 10 cpus\n",
"- in general follow patterns and avoid antipatterns by ray.io https://docs.ray.io/en/releases-1.9.0/ray-design-patterns/index.html"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "812649f9-c795-4807-83b6-a68ef087b549",
"metadata": {},
"outputs": [],
"source": [
"from typing import Optional, List, Dict, Any\n",
"\n",
"from qiskit import QuantumCircuit\n",
"from qiskit.quantum_info import SparsePauliOp\n",
"from qiskit.primitives import Estimator, EstimatorResult\n",
"from qiskit.circuit.random import random_circuit\n",
"from qiskit.opflow import X, I\n",
"\n",
"from quantum_serverless import QuantumServerless, put, get, distribute_task"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e75b99aa-62d3-489d-9bdf-7b8c64fc88f0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Cluster result EstimatorResult(values=array([2.15866755, 1.37974414, 3. ]), metadata=[{}, {}, {}])\n"
]
}
],
"source": [
"# classical funciton that will be executed as remote task\n",
"@distribute_task()\n",
"def add_prefix(circuit: QuantumCircuit, prefix_depth) -> QuantumCircuit:\n",
" return random_circuit(circuit.num_qubits, prefix_depth).compose(circuit)\n",
"\n",
"# quantum related compute that should be executed as closer to quantum as possible\n",
"# using resource QPU\n",
"@distribute_task()\n",
"def measure_exp_val(circuits: List[QuantumCircuit], observable) -> EstimatorResult:\n",
" with Estimator(circuits, observable) as estimator:\n",
" return estimator(range(len(circuits)), [0] * len(circuits))\n",
"\n",
"# this class is hiding all implementation details and ray annotation from user\n",
"class YourClass:\n",
" def __init__(self, observable, prefix_depth: int = 3):\n",
" self.prefix_depth = prefix_depth\n",
" self.observable = observable\n",
" \n",
" def routine(self, circuits: List[QuantumCircuit]) -> EstimatorResult:\n",
" new_circuits = get([\n",
" add_prefix(circuit, self.prefix_depth)\n",
" for circuit in circuits\n",
" ])\n",
" return get(measure_exp_val(new_circuits, self.observable))\n",
"\n",
"# user only interact with QuantumServerless oobject to set compute resource and \n",
"# high level YourClass class\n",
"circuits = [random_circuit(3, 3) for _ in range(3)]\n",
"observable = SparsePauliOp.from_list([(\"III\", 1), (\"IIZ\", 2), (\"IXI\", 3)])\n",
"\n",
"serverless = QuantumServerless()\n",
"\n",
"with serverless.context():\n",
" res = YourClass(observable=observable, prefix_depth=3).routine(circuits)\n",
" print(f\"Cluster result {res}\")"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -1,478 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "19592fcb-7308-4c29-a973-5b226c2c3f4e",
"metadata": {},
"source": [
"# Tutorial: converting existing code to serverless without internal code modifications"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9242f125-579b-49d7-ae01-d3e9f9cb04e1",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import warnings\n",
"\n",
"warnings.filterwarnings('ignore')\n",
"\n",
"from quantum_serverless import QuantumServerless, distribute_task, get, put"
]
},
{
"cell_type": "markdown",
"id": "0b15cefd-f7b0-409e-af0f-5d8546887bd3",
"metadata": {},
"source": [
"---\n",
"\n",
"There are couple of ways to make your code running as serverless code:\n",
"- wrapping entire functions / classes as actors or functions\n",
"- monkey-patching existing class functions to swap some parts of code"
]
},
{
"cell_type": "markdown",
"id": "d996562c-553e-4ce2-a4ff-6709357b886e",
"metadata": {},
"source": [
"### Approach #1: wrapping function / classes as tasks / actors\n",
"\n",
"We will work with qiskit and try to wrap some functions / classes as tasks / actors"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c6d593d0-435c-456a-a16e-9265c43f1d3c",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\"> ┌───┐ ┌───┐ \n",
"q_0: ─┤ X ├─┤ X ├──■─────────────────────────────\n",
" ┌┴───┴┐└─┬─┘ │ ┌─────────────────────────┐\n",
"q_1: ┤ Sdg ├──■────┼──┤ U3(1.417,2.9487,2.2238) ├\n",
" └─────┘ │ │ └─────────────────────────┘\n",
"q_2: ───■─────■────■─────────────────────────────\n",
" │ ┌─┴─┐ \n",
"q_3: ───■─────■──┤ X ├───────────────────────────\n",
" ┌─┴─┐ ┌─┴─┐├───┤ \n",
"q_4: ─┤ X ├─┤ Y ├┤ T ├───────────────────────────\n",
" └───┘ └───┘└───┘ </pre>"
],
"text/plain": [
" ┌───┐ ┌───┐ \n",
"q_0: ─┤ X ├─┤ X ├──■─────────────────────────────\n",
" ┌┴───┴┐└─┬─┘ │ ┌─────────────────────────┐\n",
"q_1: ┤ Sdg ├──■────┼──┤ U3(1.417,2.9487,2.2238) ├\n",
" └─────┘ │ │ └─────────────────────────┘\n",
"q_2: ───■─────■────■─────────────────────────────\n",
" │ ┌─┴─┐ \n",
"q_3: ───■─────■──┤ X ├───────────────────────────\n",
" ┌─┴─┐ ┌─┴─┐├───┤ \n",
"q_4: ─┤ X ├─┤ Y ├┤ T ├───────────────────────────\n",
" └───┘ └───┘└───┘ "
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from qiskit import QuantumCircuit, transpile\n",
"from qiskit.providers import Backend\n",
"from qiskit.circuit.random import random_circuit\n",
"from qiskit.providers.aer import AerSimulator\n",
"from qiskit.test.mock import FakeVigo, FakeAlmaden, FakeBrooklyn, FakeCasablanca\n",
"\n",
"circuit = random_circuit(5, 3)\n",
"backend = AerSimulator.from_backend(FakeAlmaden())\n",
"\n",
"circuit.draw()"
]
},
{
"cell_type": "markdown",
"id": "ffedccc0-ce50-4808-96ee-b5ce39222278",
"metadata": {},
"source": [
"We have transpile function. Let's start with transpilation function.\n",
"\n",
"You can call `transpile(circuits, backend)` to compile circuits to specific backend locally. "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "9887fd2e-0218-48db-9338-15c47da9dc04",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\">global phase: 0\n",
" ┌──────────┐ ┌─────────┐ ┌───┐┌──────────┐ ┌───┐┌─────────────────────────┐ \n",
" q_1 -> 9 ─┤ U1(-π/2) ├───────■─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ U1(π/4) ├─────────────────────────────────────────┤ X ├┤ U1(-π/4) ├─────┤ X ├┤ U3(1.417,2.9487,2.2238) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └──────────┘ │ ┌─────────┐ ┌───┐ ┌──────────┐┌───┐ │ └─────────┘ └─┬─┘└──┬───┬───┘ └─┬─┘└─────────────────────────┘ ┌─────────┐ ┌───┐ ┌───┐ \n",
"q_3 -> 12 ───────────────■────┼────────────────────────────────■──┤ U1(π/4) ├───────────────────────────────────────┤ X ├────┤ U1(-π/4) ├┤ X ├──────────────────┼───────────────────────────────────────────────────■────┼─────┤ X ├──────■────┼───────────────────────────────────────────■────────────────────────────────■──┤ U1(π/4) ├─────────────────────┤ X ├──■──┤ X ├──────────────────────\n",
" ┌─────────┐ ┌─┴─┐ │ ┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌───┐ ┌───┐ └─┬─┘ ├─────────┬┘└─┬─┘ │ ┌───┐ ┌─┴─┐ │ └─┬─┘ ┌─┴─┐ │ ┌─────────┐ ┌─┴─┐┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐└─┬─┘┌─┴─┐└─┬─┘┌───┐┌──────────┐┌───┐\n",
"q_4 -> 13 ─┤ U2(0,π) ├─┤ X ├──┼──┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├─────■──────┤ U1(π/4) ├───■────■───────────────┼────────────────■───────────────────■──┤ X ├──■──┤ X ├──┼───────■──────┤ X ├──┼───────────────■─────────────┤ U2(0,π) ├─┤ X ├┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├──■──┤ X ├──■──┤ X ├┤ U1(-π/4) ├┤ X ├\n",
" ┌┴─────────┴┐└───┘┌─┴─┐├──────────┤└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘└─┬─┘ │ └─┬─┘ └─────────┘ ┌─┴─┐┌─────────┐┌─┴─┐┌──────────┐┌─┴─┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐└───┘ │ ┌─────────┐ └───┘ │ │ └─────────┘ └───┘└──────────┘└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘ └───┘ └─┬─┘├─────────┬┘└─┬─┘\n",
"q_0 -> 14 ┤ U2(-π,-π) ├─────┤ X ├┤ U1(-π/4) ├──┼────────────────────────────────┼───────────────────┼────┼────┼───────────────────────────────┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├───────■──┤ U1(π/4) ├────────■───────────────┼────────────────────────────────────────────■────────────────────────────────■──────────────────────────────────■──┤ U1(π/4) ├───■──\n",
" └───────────┘ └───┘└──────────┘ │ │ │ ┌─┴─┐ │ ┌──────────┐ └───┘└─────────┘└───┘└──────────┘└───┘└─────────────┘└───┘ └───┘ └─────────┘ ┌─┴─┐ ┌──────────┐ └─────────┘ \n",
"q_2 -> 18 ─────────────────────────────────────■────────────────────────────────■───────────────────■──┤ X ├──■──┤ U1(-π/2) ├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├───────────┤ U1(3π/4) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └───┘ └──────────┘ └───┘ └──────────┘ </pre>"
],
"text/plain": [
"global phase: 0\n",
" ┌──────────┐ ┌─────────┐ ┌───┐┌──────────┐ ┌───┐┌─────────────────────────┐ \n",
" q_1 -> 9 ─┤ U1(-π/2) ├───────■─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ U1(π/4) ├─────────────────────────────────────────┤ X ├┤ U1(-π/4) ├─────┤ X ├┤ U3(1.417,2.9487,2.2238) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └──────────┘ │ ┌─────────┐ ┌───┐ ┌──────────┐┌───┐ │ └─────────┘ └─┬─┘└──┬───┬───┘ └─┬─┘└─────────────────────────┘ ┌─────────┐ ┌───┐ ┌───┐ \n",
"q_3 -> 12 ───────────────■────┼────────────────────────────────■──┤ U1(π/4) ├───────────────────────────────────────┤ X ├────┤ U1(-π/4) ├┤ X ├──────────────────┼───────────────────────────────────────────────────■────┼─────┤ X ├──────■────┼───────────────────────────────────────────■────────────────────────────────■──┤ U1(π/4) ├─────────────────────┤ X ├──■──┤ X ├──────────────────────\n",
" ┌─────────┐ ┌─┴─┐ │ ┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌───┐ ┌───┐ └─┬─┘ ├─────────┬┘└─┬─┘ │ ┌───┐ ┌─┴─┐ │ └─┬─┘ ┌─┴─┐ │ ┌─────────┐ ┌─┴─┐┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐└─┬─┘┌─┴─┐└─┬─┘┌───┐┌──────────┐┌───┐\n",
"q_4 -> 13 ─┤ U2(0,π) ├─┤ X ├──┼──┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├─────■──────┤ U1(π/4) ├───■────■───────────────┼────────────────■───────────────────■──┤ X ├──■──┤ X ├──┼───────■──────┤ X ├──┼───────────────■─────────────┤ U2(0,π) ├─┤ X ├┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├──■──┤ X ├──■──┤ X ├┤ U1(-π/4) ├┤ X ├\n",
" ┌┴─────────┴┐└───┘┌─┴─┐├──────────┤└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘└─┬─┘ │ └─┬─┘ └─────────┘ ┌─┴─┐┌─────────┐┌─┴─┐┌──────────┐┌─┴─┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐└───┘ │ ┌─────────┐ └───┘ │ │ └─────────┘ └───┘└──────────┘└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘ └───┘ └─┬─┘├─────────┬┘└─┬─┘\n",
"q_0 -> 14 ┤ U2(-π,-π) ├─────┤ X ├┤ U1(-π/4) ├──┼────────────────────────────────┼───────────────────┼────┼────┼───────────────────────────────┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├───────■──┤ U1(π/4) ├────────■───────────────┼────────────────────────────────────────────■────────────────────────────────■──────────────────────────────────■──┤ U1(π/4) ├───■──\n",
" └───────────┘ └───┘└──────────┘ │ │ │ ┌─┴─┐ │ ┌──────────┐ └───┘└─────────┘└───┘└──────────┘└───┘└─────────────┘└───┘ └───┘ └─────────┘ ┌─┴─┐ ┌──────────┐ └─────────┘ \n",
"q_2 -> 18 ─────────────────────────────────────■────────────────────────────────■───────────────────■──┤ X ├──■──┤ U1(-π/2) ├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├───────────┤ U1(3π/4) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └───┘ └──────────┘ └───┘ └──────────┘ "
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# let's transpile function to see what it does\n",
"transpiled_circuit = transpile(circuit, backend)\n",
"\n",
"transpiled_circuit.draw(idle_wires=False, fold=-1)"
]
},
{
"cell_type": "markdown",
"id": "0ab5ecfc-7f22-44f4-9e12-f1863d6e9a3a",
"metadata": {},
"source": [
"We can turn this function in ray remote function, that can be executed in parallel on configured machine / cluster"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "df0cae01-c9c5-4782-9486-42cd71342554",
"metadata": {},
"outputs": [],
"source": [
"remote_transpile = distribute_task()(transpile)"
]
},
{
"cell_type": "markdown",
"id": "69a87950-a361-4dab-8ea0-315a3aa5dd58",
"metadata": {},
"source": [
"Now we have remote transpile function, we can try it out. But before let's create serverless class which will gives us executon context."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f846d2c7-23aa-4f70-b4fb-913ebf4d6de9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<QuantumServerless | providers [local]>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"serverless = QuantumServerless()\n",
"serverless"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c45818f2-6b8b-401b-ab2f-e1b72af275af",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pointer to task: ObjectRef(c8ef45ccd0112571ffffffffffffffffffffffff0100000001000000)\n"
]
},
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\">global phase: 0\n",
" ┌──────────┐ ┌─────────┐ ┌───┐┌──────────┐ ┌───┐┌─────────────────────────┐ \n",
" q_1 -> 9 ─┤ U1(-π/2) ├───────■─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ U1(π/4) ├─────────────────────────────────────────┤ X ├┤ U1(-π/4) ├─────┤ X ├┤ U3(1.417,2.9487,2.2238) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └──────────┘ │ ┌─────────┐ ┌───┐ ┌──────────┐┌───┐ │ └─────────┘ ┌───┐└─┬─┘└──────────┘┌───┐└─┬─┘└─────────────────────────┘ ┌─────────┐ ┌───┐ \n",
"q_3 -> 12 ───────────────■────┼────────────────────────────────■──┤ U1(π/4) ├───────────────────────────────────────┤ X ├────┤ U1(-π/4) ├┤ X ├──────────────────┼─────────────────────────────────────────────────┤ X ├──┼───────■──────┤ X ├──┼───────────────────────────────────────────■────────────────────────────────■──┤ U1(π/4) ├───────────────────────■──┤ X ├──■────────────────────────\n",
" ┌─────────┐ ┌─┴─┐ │ ┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌───┐ ┌───┐ └─┬─┘ ├─────────┬┘└─┬─┘ │ ┌───┐ └─┬─┘ │ ┌─┴─┐ └─┬─┘ │ ┌─────────┐ ┌─┴─┐┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐┌───┐┌──────────┐┌───┐\n",
"q_4 -> 13 ─┤ U2(0,π) ├─┤ X ├──┼──┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├─────■──────┤ U1(π/4) ├───■────■───────────────┼────────────────■───────────────────■──┤ X ├──■────■────┼─────┤ X ├──────■────┼───────────────■─────────────┤ U2(0,π) ├─┤ X ├┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├┤ X ├┤ U1(-π/4) ├┤ X ├\n",
" ┌┴─────────┴┐└───┘┌─┴─┐├──────────┤└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘└─┬─┘ │ └─┬─┘ └─────────┘ ┌─┴─┐┌─────────┐┌─┴─┐┌──────────┐┌─┴─┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐ │ ┌──┴───┴──┐ │ │ └─────────┘ └───┘└──────────┘└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘└───┘ └───┘└─┬─┘├─────────┬┘└─┬─┘\n",
"q_0 -> 14 ┤ U2(-π,-π) ├─────┤ X ├┤ U1(-π/4) ├──┼────────────────────────────────┼───────────────────┼────┼────┼───────────────────────────────┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├───────■──┤ U1(π/4) ├────────■───────────────┼────────────────────────────────────────────■────────────────────────────────■──────────────────────────────────■──┤ U1(π/4) ├───■──\n",
" └───────────┘ └───┘└──────────┘ │ │ │ ┌─┴─┐ │ ┌──────────┐ └───┘└─────────┘└───┘└──────────┘└───┘└─────────────┘└───┘ └───┘ └─────────┘ ┌─┴─┐ ┌──────────┐ └─────────┘ \n",
"q_2 -> 18 ─────────────────────────────────────■────────────────────────────────■───────────────────■──┤ X ├──■──┤ U1(-π/2) ├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├───────────┤ U1(3π/4) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └───┘ └──────────┘ └───┘ └──────────┘ </pre>"
],
"text/plain": [
"global phase: 0\n",
" ┌──────────┐ ┌─────────┐ ┌───┐┌──────────┐ ┌───┐┌─────────────────────────┐ \n",
" q_1 -> 9 ─┤ U1(-π/2) ├───────■─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ U1(π/4) ├─────────────────────────────────────────┤ X ├┤ U1(-π/4) ├─────┤ X ├┤ U3(1.417,2.9487,2.2238) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └──────────┘ │ ┌─────────┐ ┌───┐ ┌──────────┐┌───┐ │ └─────────┘ ┌───┐└─┬─┘└──────────┘┌───┐└─┬─┘└─────────────────────────┘ ┌─────────┐ ┌───┐ \n",
"q_3 -> 12 ───────────────■────┼────────────────────────────────■──┤ U1(π/4) ├───────────────────────────────────────┤ X ├────┤ U1(-π/4) ├┤ X ├──────────────────┼─────────────────────────────────────────────────┤ X ├──┼───────■──────┤ X ├──┼───────────────────────────────────────────■────────────────────────────────■──┤ U1(π/4) ├───────────────────────■──┤ X ├──■────────────────────────\n",
" ┌─────────┐ ┌─┴─┐ │ ┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌───┐ ┌───┐ └─┬─┘ ├─────────┬┘└─┬─┘ │ ┌───┐ └─┬─┘ │ ┌─┴─┐ └─┬─┘ │ ┌─────────┐ ┌─┴─┐┌──────────┐┌───┐┌─────────┐┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐┌───┐┌──────────┐┌───┐\n",
"q_4 -> 13 ─┤ U2(0,π) ├─┤ X ├──┼──┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├─────■──────┤ U1(π/4) ├───■────■───────────────┼────────────────■───────────────────■──┤ X ├──■────■────┼─────┤ X ├──────■────┼───────────────■─────────────┤ U2(0,π) ├─┤ X ├┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├┤ X ├┤ U1(-π/4) ├┤ X ├\n",
" ┌┴─────────┴┐└───┘┌─┴─┐├──────────┤└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘└─┬─┘ │ └─┬─┘ └─────────┘ ┌─┴─┐┌─────────┐┌─┴─┐┌──────────┐┌─┴─┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐ │ ┌──┴───┴──┐ │ │ └─────────┘ └───┘└──────────┘└─┬─┘└─────────┘└───┘└──────────┘└─┬─┘└─────────────┘└───┘ └───┘└─┬─┘├─────────┬┘└─┬─┘\n",
"q_0 -> 14 ┤ U2(-π,-π) ├─────┤ X ├┤ U1(-π/4) ├──┼────────────────────────────────┼───────────────────┼────┼────┼───────────────────────────────┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├───────■──┤ U1(π/4) ├────────■───────────────┼────────────────────────────────────────────■────────────────────────────────■──────────────────────────────────■──┤ U1(π/4) ├───■──\n",
" └───────────┘ └───┘└──────────┘ │ │ │ ┌─┴─┐ │ ┌──────────┐ └───┘└─────────┘└───┘└──────────┘└───┘└─────────────┘└───┘ └───┘ └─────────┘ ┌─┴─┐ ┌──────────┐ └─────────┘ \n",
"q_2 -> 18 ─────────────────────────────────────■────────────────────────────────■───────────────────■──┤ X ├──■──┤ U1(-π/2) ├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├───────────┤ U1(3π/4) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────\n",
" └───┘ └──────────┘ └───┘ └──────────┘ "
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"with serverless.context():\n",
" # execute remote transpile function and get back pointer to remote task, \n",
" # so we can fetch results out of it\n",
" task = remote_transpile(circuit, backend)\n",
" print(f\"Pointer to task: {task}\")\n",
" \n",
" # get actual results from task, \n",
" # which will be our transpiled circuit\n",
" remotely_transpiled_circuits = get(task)\n",
"\n",
"remotely_transpiled_circuits.draw(idle_wires=False, fold=-1)"
]
},
{
"cell_type": "markdown",
"id": "76457206-e976-4dc7-a834-60364494cb79",
"metadata": {},
"source": [
"Because we have this function as ray remote function, we can run multiple of them in parallel"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "8295a707-0a1c-460c-afdb-c949062bbd98",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\">global phase: 3.1306\n",
" ┌───┐ ┌───┐┌─────────────────────┐ ┌─────────┐ ┌───┐ ┌───┐ \n",
" q_3 -> 9 ─────────────────────┤ X ├─────────■───────┤ X ├┤ U3(1.1556,π/2,-π/2) ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■────────────────────────────────■──────────────────────────────────■──┤ U1(π/4) ├───■───────────────────────────────────────────────────────────────────────────────────────────────┤ X ├────────────■───────┤ X ├─────────────────────────────────────────────────────────────────────────────────────\n",
" └─┬─┘ │ └─┬─┘└─────────────────────┘ ┌─────────┐ ┌───┐┌──────────┐┌───┐┌───────────────────────────┐ │ │ │ └─────────┘ │ └─┬─┘ │ └─┬─┘ \n",
"q_0 -> 12 ───────────────────────┼───────────┼─────────┼───────────────────────────■──┤ U1(π/4) ├────────────────────────────────────┤ X ├┤ U1(-π/4) ├┤ X ├┤ U3(2.2178,2.7588,0.70376) ├──────────────────────────────┼────────────────────────────────┼──────────────────────────────────┼────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────\n",
" ┌─────────────┐ │ │ ┌───┐ │ ┌─────────┐ ┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌───┐ ┌───┐└─┬─┘├─────────┬┘└─┬─┘└───────────┬───┬───────────┘ │ ┌─────────┐ │ ┌───┐ │ │ ┌────────────────────────┐┌───┐┌───────────────────────┐┌───┐ ┌──────────────────────┐ │ ┌───┐ │ ┌───┐ │ ┌───────────────────────┐ ┌───────────────┐ \n",
"q_4 -> 13 ┤ U2(-π/4,-π) ├────────┼───────────┼──┤ X ├──┼────────┤ U1(π/4) ├──────┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,1.641) ├┤ X ├──■──┤ X ├──■──┤ U1(π/4) ├───■──────────────┤ X ├─────────────────────────■────────────────┼───────────────■──┤ U1(π/4) ├───┼───────────────────■──┤ X ├──■────┼────────────────┼────■──┤ U1(-0.944196508933282) ├┤ X ├┤ U3(1.0866,-π,0.12636) ├┤ X ├──┤ U3(1.0866,0.81784,0) ├───┼──┤ X ├──■────┼──┤ X ├──┼──┤ U1(0.627327644292588) ├───■────────────────────────────■──────┤ U3(π,π/2,π/2) ├────\n",
" └─────────────┘ │ ┌─┴─┐└─┬─┘ │ └┬───────┬┘ └───┘└──────────┘└─┬─┘└─────────────┘└─┬─┘ │ └─┬─┘ └─────────┘ └─┬─┘ ┌─────────┐┌─┴─┐┌──────────┐┌─┴─┐┌─────────┐┌─┴─┐├─────────┴┐┌─┴─┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐┌─┴─┐┌──────────┐┌─┴─┐┌─┴─┐└┬──────────────────────┬┘└─┬─┘└───────────────────────┘└─┬─┘┌─┴──────────────────────┴┐ │ └─┬─┘ │ ┌─┴─┐└─┬─┘ │ ├───────────────────────┴┐┌─┴─┐┌──────────────────────┐┌─┴─┐┌───┴───────────────┴───┐\n",
"q_2 -> 14 ───────────────────────■─────────┤ X ├──┼────■─────────┤ U1(π) ├──────────────────────────┼───────────────────┼────┼────┼──────────────────────────────────────■──────────────┤ U2(0,π) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ X ├─┤ U1(3.01523521327176) ├───■─────────────────────────────■──┤ U3(2.724,0.99285,1.685) ├──■────┼────┼──┤ X ├──┼────■──┤ U1(-0.238171820801467) ├┤ X ├┤ U3(2.2928,-π,2.5143) ├┤ X ├┤ U3(2.2928,0.080101,0) ├\n",
" ┌────────────────┐└───┘ │ └───────┘ │ │ ┌─┴─┐ │ └─────────┘└───┘└──────────┘└───┘└─────────┘└───┘└──────────┘└───┘└─────────────┘└───┘ └───┘└───┘└──────────┘└───┘└───┘ └──────────────────────┘ └─────────────────────────┘ │ ┌─┴─┐└───┘ │ └────────────────────────┘└───┘└──────────────────────┘└───┘└───────────────────────┘\n",
"q_1 -> 18 ───────────────┤ U3(0.3885,0,0) ├───────■─────────────────────────────────────────────────■───────────────────■──┤ X ├──■───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ X ├───────■────────────────────────────────────────────────────────────────────────────────────────────\n",
" └────────────────┘ └───┘ └───┘ </pre>"
],
"text/plain": [
"global phase: 3.1306\n",
" ┌───┐ ┌───┐┌─────────────────────┐ ┌─────────┐ ┌───┐ ┌───┐ \n",
" q_3 -> 9 ─────────────────────┤ X ├─────────■───────┤ X ├┤ U3(1.1556,π/2,-π/2) ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■────────────────────────────────■──────────────────────────────────■──┤ U1(π/4) ├───■───────────────────────────────────────────────────────────────────────────────────────────────┤ X ├────────────■───────┤ X ├─────────────────────────────────────────────────────────────────────────────────────\n",
" └─┬─┘ │ └─┬─┘└─────────────────────┘ ┌─────────┐ ┌───┐┌──────────┐┌───┐┌───────────────────────────┐ │ │ │ └─────────┘ │ └─┬─┘ │ └─┬─┘ \n",
"q_0 -> 12 ───────────────────────┼───────────┼─────────┼───────────────────────────■──┤ U1(π/4) ├────────────────────────────────────┤ X ├┤ U1(-π/4) ├┤ X ├┤ U3(2.2178,2.7588,0.70376) ├──────────────────────────────┼────────────────────────────────┼──────────────────────────────────┼────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────\n",
" ┌─────────────┐ │ │ ┌───┐ │ ┌─────────┐ ┌─┴─┐├─────────┴┐┌───┐┌─────────────┐┌───┐ ┌───┐└─┬─┘├─────────┬┘└─┬─┘└───────────┬───┬───────────┘ │ ┌─────────┐ │ ┌───┐ │ │ ┌────────────────────────┐┌───┐┌───────────────────────┐┌───┐ ┌──────────────────────┐ │ ┌───┐ │ ┌───┐ │ ┌───────────────────────┐ ┌───────────────┐ \n",
"q_4 -> 13 ┤ U2(-π/4,-π) ├────────┼───────────┼──┤ X ├──┼────────┤ U1(π/4) ├──────┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,1.641) ├┤ X ├──■──┤ X ├──■──┤ U1(π/4) ├───■──────────────┤ X ├─────────────────────────■────────────────┼───────────────■──┤ U1(π/4) ├───┼───────────────────■──┤ X ├──■────┼────────────────┼────■──┤ U1(-0.944196508933282) ├┤ X ├┤ U3(1.0866,-π,0.12636) ├┤ X ├──┤ U3(1.0866,0.81784,0) ├───┼──┤ X ├──■────┼──┤ X ├──┼──┤ U1(0.627327644292588) ├───■────────────────────────────■──────┤ U3(π,π/2,π/2) ├────\n",
" └─────────────┘ │ ┌─┴─┐└─┬─┘ │ └┬───────┬┘ └───┘└──────────┘└─┬─┘└─────────────┘└─┬─┘ │ └─┬─┘ └─────────┘ └─┬─┘ ┌─────────┐┌─┴─┐┌──────────┐┌─┴─┐┌─────────┐┌─┴─┐├─────────┴┐┌─┴─┐┌─────────────┐┌─┴─┐└─┬─┘┌─┴─┐┌─┴─┐┌──────────┐┌─┴─┐┌─┴─┐└┬──────────────────────┬┘└─┬─┘└───────────────────────┘└─┬─┘┌─┴──────────────────────┴┐ │ └─┬─┘ │ ┌─┴─┐└─┬─┘ │ ├───────────────────────┴┐┌─┴─┐┌──────────────────────┐┌─┴─┐┌───┴───────────────┴───┐\n",
"q_2 -> 14 ───────────────────────■─────────┤ X ├──┼────■─────────┤ U1(π) ├──────────────────────────┼───────────────────┼────┼────┼──────────────────────────────────────■──────────────┤ U2(0,π) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U1(π/4) ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ U2(0,-3π/4) ├┤ X ├──■──┤ X ├┤ X ├┤ U1(-π/4) ├┤ X ├┤ X ├─┤ U1(3.01523521327176) ├───■─────────────────────────────■──┤ U3(2.724,0.99285,1.685) ├──■────┼────┼──┤ X ├──┼────■──┤ U1(-0.238171820801467) ├┤ X ├┤ U3(2.2928,-π,2.5143) ├┤ X ├┤ U3(2.2928,0.080101,0) ├\n",
" ┌────────────────┐└───┘ │ └───────┘ │ │ ┌─┴─┐ │ └─────────┘└───┘└──────────┘└───┘└─────────┘└───┘└──────────┘└───┘└─────────────┘└───┘ └───┘└───┘└──────────┘└───┘└───┘ └──────────────────────┘ └─────────────────────────┘ │ ┌─┴─┐└───┘ │ └────────────────────────┘└───┘└──────────────────────┘└───┘└───────────────────────┘\n",
"q_1 -> 18 ───────────────┤ U3(0.3885,0,0) ├───────■─────────────────────────────────────────────────■───────────────────■──┤ X ├──■───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ X ├───────■────────────────────────────────────────────────────────────────────────────────────────────\n",
" └────────────────┘ └───┘ └───┘ "
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"with serverless.context():\n",
" # let's run 5 circuits transpilations in parallel\n",
" tasks = [\n",
" remote_transpile(random_circuit(5, i + 1), backend)\n",
" for i in range(5)\n",
" ]\n",
"\n",
" # get all results when ready\n",
" transpiled_circuits = get(tasks)\n",
" \n",
"# look at our final transpiled circuit\n",
"transpiled_circuits[-1].draw(idle_wires=False, fold=-1)"
]
},
{
"cell_type": "markdown",
"id": "d2541d01-7dc7-4f81-9406-e5217dd94c6a",
"metadata": {},
"source": [
"### Approach #2: Monkey-patching\n",
"\n",
"Python is allowing you to change definitions of classes and function in a runtime.\n",
"This is useful if you need to patch a small chunk of class with your implementation.\n",
"In our case we will want to swap chunks of code that can be run in parallel with our ray calls."
]
},
{
"cell_type": "markdown",
"id": "bc2cbf84-64b3-4788-95f2-a3cbbc954dd4",
"metadata": {},
"source": [
"Let's create a dummy class that we will be monkey-patching later"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "fc2ff5fc-b0d7-4e37-98ac-9d1ef40e214e",
"metadata": {},
"outputs": [],
"source": [
"class DummyClass:\n",
" def sum_n_times(self, a: int, b: int, n_times: int):\n",
" \"\"\"Do something n times.\"\"\"\n",
" \n",
" result = []\n",
" for i in range(n_times):\n",
" result.append(a + b)\n",
" return result "
]
},
{
"cell_type": "markdown",
"id": "dc9db34a-44f7-4a63-81f4-8e3e41306775",
"metadata": {},
"source": [
"---\n",
"Let's write patch function"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "a0ab28d9-9643-42ac-8c48-fede8e2ea35b",
"metadata": {},
"outputs": [],
"source": [
"@distribute_task()\n",
"def sum_remote(a: int, b: int):\n",
" return a + b\n",
"\n",
"def sum_n_times_patch(self, a: int, b: int, n_times: int):\n",
" return get([\n",
" sum_remote(a, b)\n",
" for _ in range(n_times)\n",
" ]) "
]
},
{
"cell_type": "markdown",
"id": "0d7a3244-6280-4f41-9f42-b13798b191fc",
"metadata": {},
"source": [
"Patch it now"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "da462ace-3e52-460e-827a-4604f747cfd0",
"metadata": {},
"outputs": [],
"source": [
"DummyClass.sum_n_times = sum_n_times_patch"
]
},
{
"cell_type": "markdown",
"id": "2c8a1c83-f6b9-488d-adaa-8890ec450c71",
"metadata": {},
"source": [
"Now we need to create instance of our patched class and run it"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "038f6a93-034c-46e2-b432-06b464391b77",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]\n"
]
}
],
"source": [
"dummy_class = DummyClass()\n",
"\n",
"with serverless.context():\n",
" result = dummy_class.sum_n_times(1, 1, n_times=10)\n",
" print(result)"
]
},
{
"cell_type": "markdown",
"id": "5a880c08-aa14-4a57-8ec6-03976ba18c78",
"metadata": {},
"source": [
"Function is leveraging parallelization internally now"
]
}
],
"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.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}