Issue 217 | Docs: getting started 3 level tutorials (#222)
* Issue 217 | Docs: getting started 3 level tutorials
This commit is contained in:
parent
6fd956f429
commit
e85392ded7
137
README.md
137
README.md
|
@ -6,6 +6,11 @@
|
|||
|
||||
# Quantum serverless
|
||||
|
||||
Quantum Serverless is a user-friendly tool that enables you to easily run complex quantum computing tasks.
|
||||
With this software, you can execute Qiskit programs as long running jobs and distribute them across multiple CPUs, GPUs, and QPUs.
|
||||
This means you can take on more complex quantum-classical programs and run them with ease.
|
||||
You don't have to worry about configuration or scaling up computational resources, as Quantum Serverless takes care of everything for you.
|
||||
|
||||
![diagram](./docs/images/qs_diagram.png)
|
||||
|
||||
### Table of Contents
|
||||
|
@ -13,139 +18,31 @@
|
|||
1. [Installation](INSTALL.md)
|
||||
2. [Quickstart](#quickstart-guide)
|
||||
3. [Beginners Guide](docs/beginners_guide.md)
|
||||
4. Modules:
|
||||
4. [Getting started](docs/getting_started/)
|
||||
5. Modules:
|
||||
1. [Client](./client)
|
||||
2. [Infrastructure](./infrastructure)
|
||||
5. [Tutorials](docs/tutorials/)
|
||||
6. [Guides](docs/guides/)
|
||||
7. [How to Give Feedback](#how-to-give-feedback)
|
||||
8. [Contribution Guidelines](#contribution-guidelines)
|
||||
9. [References and Acknowledgements](#references-and-acknowledgements)
|
||||
10. [License](#license)
|
||||
6. [Tutorials](docs/tutorials/)
|
||||
7. [Guides](docs/guides/)
|
||||
8. [How to Give Feedback](#how-to-give-feedback)
|
||||
9. [Contribution Guidelines](#contribution-guidelines)
|
||||
10. [References and Acknowledgements](#references-and-acknowledgements)
|
||||
11. [License](#license)
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
### Quickstart
|
||||
|
||||
Steps
|
||||
1. prepare infrastructure
|
||||
2. write your program
|
||||
3. run program
|
||||
|
||||
#### Prepare infrastructure
|
||||
|
||||
In the root folder of this project you can find `docker-compose.yml`
|
||||
file, which is configured to run all necessary services for quickstart tutorials.
|
||||
|
||||
Run in a root folder
|
||||
1. Prepare local infrastructure
|
||||
```shell
|
||||
docker-compose pull
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
:memo: For more advanced ways to deploy the project you have the guide:
|
||||
[Multi cloud deployment](https://qiskit-extensions.github.io/quantum-serverless/guides/08_multi_cloud_deployment.html).
|
||||
|
||||
#### Write your program
|
||||
|
||||
Create python file with necessary code. Let's call in `program.py`
|
||||
|
||||
```python
|
||||
# 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
|
||||
from quantum_serverless.core.state import RedisStateHandler
|
||||
|
||||
# 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()
|
||||
|
||||
# 2.1 (Optional) state handler to write/read results in/out of job
|
||||
state_handler = RedisStateHandler("redis", 6379)
|
||||
|
||||
circuits = [random_circuit(2, 2) for _ in range(3)]
|
||||
|
||||
# 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. 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
|
||||
single_result = get(function_reference)
|
||||
parallel_result = get(function_references)
|
||||
print("Single execution:", single_result)
|
||||
print("N parallel executions:", parallel_result)
|
||||
|
||||
# 5.1 (Optional) write results to state.
|
||||
state_handler.set("result", {
|
||||
"status": "ok",
|
||||
"single": single_result.tolist(),
|
||||
"parallel_result": [entry.tolist() for entry in parallel_result]
|
||||
})
|
||||
```
|
||||
|
||||
#### Run program
|
||||
|
||||
Let's run our program now
|
||||
|
||||
```python
|
||||
from quantum_serverless import QuantumServerless, Program
|
||||
from quantum_serverless.core.state import RedisStateHandler
|
||||
|
||||
serverless = QuantumServerless({
|
||||
"providers": [{
|
||||
"name": "docker-compose",
|
||||
"compute_resource": {
|
||||
"name": "docker-compose",
|
||||
"host": "localhost", # using our docker-compose infrastructure
|
||||
}
|
||||
}]
|
||||
})
|
||||
serverless.set_provider("docker-compose") # set provider as docker-compose
|
||||
|
||||
state_handler = RedisStateHandler("localhost", 6379)
|
||||
|
||||
# create out program
|
||||
program = Program(
|
||||
name="my_program",
|
||||
entrypoint="program.py" # set entrypoint as our program.py file
|
||||
)
|
||||
|
||||
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])]
|
||||
|
||||
state_handler.get("result") # (Optional) get written data
|
||||
# {'status': 'ok',
|
||||
# 'single': [1.0],
|
||||
# 'parallel_result': [[1.0], [0.0], [-0.28650496]]}
|
||||
```
|
||||
2. Open jupyter notebook in browser at [http://localhost:8888/](http://localhost:8888/). Password for notebook is `123`
|
||||
3. Explore 3 getting started tutorials.
|
||||
|
||||
For more detailed examples and explanations refer to [Beginners Guide](docs/beginners_guide.md), [Getting started examples](docs/getting_started/), [Guides](docs/guides) and [Tutorials](docs/tutorials).
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ services:
|
|||
jupyter:
|
||||
container_name: qs-jupyter
|
||||
image: qiskit/quantum-serverless-notebook:nightly-py39
|
||||
volumes:
|
||||
- ./docs/getting_started:/home/jovyan/
|
||||
ports:
|
||||
- 8888:8888
|
||||
environment:
|
||||
|
@ -73,7 +75,7 @@ services:
|
|||
KEYCLOAK_ADMIN_USER: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: 123
|
||||
KEYCLOAK_HTTP_PORT: 8080
|
||||
KEYCLOAK_EXTRA_ARGS: "--import-realm"
|
||||
KEYCLOAK_EXTRA_ARGS: "-Dkeycloak.import=/opt/keycloak/data/import/realm-export.json"
|
||||
ports:
|
||||
- '8085:8080'
|
||||
depends_on:
|
||||
|
@ -85,19 +87,20 @@ services:
|
|||
gateway:
|
||||
container_name: gateway
|
||||
image: docker.io/qiskit/quantum-serverless-gateway:nightly
|
||||
command: gunicorn gateway.wsgi:application --bind 0.0.0.0:8000 --workers=3
|
||||
command: python manage.py runserver 0.0.0.0:8000
|
||||
ports:
|
||||
- 8000:8000
|
||||
environment:
|
||||
- DEBUG=0
|
||||
- RAY_HOST=http://ray-head:8265
|
||||
- CLIENT_ID=gateway-client
|
||||
- CLIENT_ID=rayclient
|
||||
- DJANGO_SUPERUSER_USERNAME=admin
|
||||
- DJANGO_SUPERUSER_PASSWORD=123
|
||||
- DJANGO_SUPERUSER_EMAIL=admin@noemail.com
|
||||
- SETTING_KEYCLOAK_URL=http://keycloak:8080/auth
|
||||
- SETTING_KEYCLOAK_REALM=Test
|
||||
- SETTINGS_KEYCLOAK_CLIENT_SECRET=AQ3sZ4eiF7NhOtfxeUEGo0YN7uQBoUnO
|
||||
- SETTING_KEYCLOAK_URL=http://keycloak:8080/
|
||||
- SETTING_KEYCLOAK_REALM=quantumserverless
|
||||
- SETTINGS_KEYCLOAK_CLIENT_SECRET=supersecret
|
||||
- SETTINGS_KEYCLOAK_CLIENT_NAME=rayclient
|
||||
- SITE_HOST=http://gateway:8000
|
||||
networks:
|
||||
- safe-tier
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "66030e20-b384-4dcf-9c5f-7664f7ad1693",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Getting started - level 1\n",
|
||||
"\n",
|
||||
"Let's write `Hello World` program using quantum serverless. \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",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"# source_files/gs_level_1.py\n",
|
||||
"\n",
|
||||
"from qiskit import QuantumCircuit\n",
|
||||
"from qiskit.primitives import Sampler\n",
|
||||
"\n",
|
||||
"circuit = QuantumCircuit(2)\n",
|
||||
"circuit.h(0)\n",
|
||||
"circuit.cx(0, 1)\n",
|
||||
"circuit.measure_all()\n",
|
||||
"circuit.draw()\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",
|
||||
"\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",
|
||||
"\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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "81dd7807-7180-4b87-bbf9-832b7cf29d69",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from quantum_serverless import QuantumServerless, GatewayProvider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "acdec789-4967-48ee-8f6c-8d2b0ff57e91",
|
||||
"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": "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",
|
||||
"\n",
|
||||
"`Program` accepts couple of required parameters:\n",
|
||||
"- name - name of the program\n",
|
||||
"- entrypoint - name of python file you want to execute\n",
|
||||
"- working_dir - folder where your script is located. This is optional parameter and will be current folder by default. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "d51df836-3f22-467c-b637-5803145d5d8a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Job | e4a801d2-f9eb-4392-b584-cbdd600755c8>"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"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",
|
||||
")\n",
|
||||
"\n",
|
||||
"job = serverless.run_program(program)\n",
|
||||
"job"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "39ee31d2-3553-4e19-bcb9-4cccd0df0e4c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"As result of `run_program` call we get `Job` which has `status` method to check status of program execution, `logs` to get logs of execution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "cc7ccea6-bbae-4184-ba7f-67b6c20a0b0b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'SUCCEEDED'"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"job.status()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "ca76abfa-2ff5-425b-a225-058d91348e8b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Quasi distribution: {0: 0.4999999999999999, 3: 0.4999999999999999}\\n'"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"job.logs()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3b1113ef-e8ad-4ed9-b07b-9da2f2b9ea1c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Also this object has `job_id` property that can be used if you want to access job results later.\n",
|
||||
"To do so we need to call `get_job_by_id` method of `QuantumServerless` client."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "f942b76d-596c-4384-8f36-e5f73e72cefd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'e4a801d2-f9eb-4392-b584-cbdd600755c8'"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"job.job_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a92069ba-0a3c-4c9f-8e8d-3916a2eb2093",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Users can fetch previously ran jobs from configured providers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "45e2927f-655b-47a4-8003-f16e5ba0a1cd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Job | e4a801d2-f9eb-4392-b584-cbdd600755c8>"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"serverless.get_job_by_id(job.job_id)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
|
@ -0,0 +1,436 @@
|
|||
{
|
||||
"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",
|
||||
"import argparse\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",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" parser = argparse.ArgumentParser()\n",
|
||||
" parser.add_argument(\n",
|
||||
" \"--bond_length\",\n",
|
||||
" help=\"Bond length in Angstrom.\",\n",
|
||||
" default=2.5,\n",
|
||||
" type=float,\n",
|
||||
" )\n",
|
||||
" args = parser.parse_args()\n",
|
||||
"\n",
|
||||
" print(f\"Running for bond length {args.bond_length}.\")\n",
|
||||
" run(args.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 argument parsing to accept arguments to our program. 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` and `arguments` to our `Program` construction.\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 | 40754290-9768-4ba2-9689-6d72e9197878>"
|
||||
]
|
||||
},
|
||||
"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",
|
||||
" arguments={\n",
|
||||
" \"bond_length\": 2.55\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"job = serverless.run_program(program)\n",
|
||||
"job"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "420f2711-b8c6-4bf9-8651-c9d098348467",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'SUCCEEDED'"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"job.status()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "7e3b0cc7-2f08-4b69-a266-bbbe4e9a6c59",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Running for bond length 2.55.\n",
|
||||
"=== GROUND STATE ENERGY ===\n",
|
||||
" \n",
|
||||
"* Electronic ground state energy (Hartree): -8.211426461751\n",
|
||||
" - computed part: -8.211426461751\n",
|
||||
" - ActiveSpaceTransformer 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.53218981]\n",
|
||||
" - computed part: [0.0 0.0 1.53218981]\n",
|
||||
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
|
||||
" > Dipole moment (a.u.): [0.0 0.0 3.28661181] Total: 3.28661181\n",
|
||||
" (debye): [0.0 0.0 8.35373319] Total: 8.35373319\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": 10,
|
||||
"id": "5f4d4317-bcc9-4e1a-942a-a38ca5331261",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[<Job | ddb7331c-6db2-4e44-9449-b58ba53bd6b8>,\n",
|
||||
" <Job | 52f77d9d-c669-4526-8b33-2fa9290b71dd>,\n",
|
||||
" <Job | c31428f3-8c15-4257-a53d-fd899d40e29f>]"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"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",
|
||||
" arguments={\n",
|
||||
" \"bond_length\": bond_length\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" jobs.append(serverless.run_program(program))\n",
|
||||
"\n",
|
||||
"jobs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "46fe3955-ac35-43d9-a5de-2a4e2cad1483",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"RUNNING\n",
|
||||
"RUNNING\n",
|
||||
"RUNNING\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for job in jobs:\n",
|
||||
" print(job.status())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "0988d6b4-03a4-4c87-ad1f-5b0526a7527e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Running for bond length 2.55.\n",
|
||||
"=== GROUND STATE ENERGY ===\n",
|
||||
" \n",
|
||||
"* Electronic ground state energy (Hartree): -8.211426461751\n",
|
||||
" - computed part: -8.211426461751\n",
|
||||
" - ActiveSpaceTransformer 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.53218981]\n",
|
||||
" - computed part: [0.0 0.0 1.53218981]\n",
|
||||
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
|
||||
" > Dipole moment (a.u.): [0.0 0.0 3.28661181] Total: 3.28661181\n",
|
||||
" (debye): [0.0 0.0 8.35373319] Total: 8.35373319\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"Running for bond length 3.0.\n",
|
||||
"=== GROUND STATE ENERGY ===\n",
|
||||
" \n",
|
||||
"* Electronic ground state energy (Hartree): -8.124024370249\n",
|
||||
" - computed part: -8.124024370249\n",
|
||||
" - ActiveSpaceTransformer 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.91821215]\n",
|
||||
" - computed part: [0.0 0.0 2.91821215]\n",
|
||||
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
|
||||
" > Dipole moment (a.u.): [0.0 0.0 2.75096622] Total: 2.75096622\n",
|
||||
" (debye): [0.0 0.0 6.99225801] Total: 6.99225801\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"Running for bond length 3.55.\n",
|
||||
"=== GROUND STATE ENERGY ===\n",
|
||||
" \n",
|
||||
"* Electronic ground state energy (Hartree): -8.049823374615\n",
|
||||
" - computed part: -8.049823374615\n",
|
||||
" - ActiveSpaceTransformer 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.39179177]\n",
|
||||
" - computed part: [0.0 0.0 5.39179177]\n",
|
||||
" - ActiveSpaceTransformer extracted energy part: [0.0 0.0 0.0]\n",
|
||||
" > Dipole moment (a.u.): [0.0 0.0 1.31673597] Total: 1.31673597\n",
|
||||
" (debye): [0.0 0.0 3.34680868] Total: 3.34680868\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.9.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
{
|
||||
"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, run_qiskit_remote, 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 `run_qiskit_remote` decorator\n",
|
||||
"@run_qiskit_remote()\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 funcitons 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 `run_qiskit_remote` decorator to convert our function to asyncronous distributed one. \n",
|
||||
"With that `my_function` is converted into asyncronous 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": 3,
|
||||
"id": "9130c64a-1e7f-4d08-afff-b2905b2d95ad",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from quantum_serverless import QuantumServerless, GatewayProvider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "0f22daae-9f0e-4f7a-8a1f-5ade989d8be9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<QuantumServerless | providers [gateway-provider]>"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"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": 6,
|
||||
"id": "f556dd85-35da-48d1-9ae1-f04a386544d9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Job | 12>"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"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": 7,
|
||||
"id": "2de3fd64-9010-48d9-ac7c-f46a7b36ba81",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'SUCCEEDED'"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"job.status()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "d6586e7a-388b-42cc-a860-abd4f6d514b9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Single execution: [1.]\n",
|
||||
"N parallel executions: [array([1.]), array([0.97400357]), array([1.])]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(job.logs())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": 9,
|
||||
"id": "1fb8931f-c8e2-49dd-923f-16fa3a7a5feb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'{\"status\": \"ok\", \"single\": [1.0], \"parallel_result\": [[1.0], [0.9740035726118753], [1.0]]}'"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"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
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.. nbgallery::
|
||||
:glob:
|
||||
|
||||
*
|
|
@ -0,0 +1,14 @@
|
|||
from qiskit import QuantumCircuit
|
||||
from qiskit.primitives import Sampler
|
||||
|
||||
circuit = QuantumCircuit(2)
|
||||
circuit.h(0)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure_all()
|
||||
circuit.draw()
|
||||
|
||||
sampler = Sampler()
|
||||
|
||||
quasi_dists = sampler.run(circuit).result().quasi_dists
|
||||
|
||||
print(f"Quasi distribution: {quasi_dists[0]}")
|
|
@ -0,0 +1,69 @@
|
|||
import argparse
|
||||
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--bond_length",
|
||||
help="Bond length in Angstrom.",
|
||||
default=2.5,
|
||||
type=float,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"Running for bond length {args.bond_length}.")
|
||||
run(args.bond_length)
|
|
@ -0,0 +1,48 @@
|
|||
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):
|
||||
"""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 funcitons in parallel
|
||||
serverless = QuantumServerless()
|
||||
|
||||
circuits = [random_circuit(2, 2) for _ in range(3)]
|
||||
|
||||
# 3. create serverless context which will allow us to run funcitons 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]
|
||||
})
|
|
@ -12,6 +12,14 @@ The source code to the project is available `on GitHub <https://github.com/Qiski
|
|||
|
||||
**Quickstart**
|
||||
|
||||
Step 0: install package
|
||||
|
||||
.. code-block::
|
||||
:caption: pip install
|
||||
|
||||
pip install quantum_serverless
|
||||
|
||||
|
||||
Step 1: run infrastructure
|
||||
|
||||
.. code-block::
|
||||
|
@ -99,6 +107,13 @@ Step 3: run program
|
|||
|
||||
------------
|
||||
|
||||
**Getting started**
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Guides <getting_started/index>
|
||||
|
||||
**Guides**
|
||||
|
||||
.. toctree::
|
||||
|
|
|
@ -19,6 +19,7 @@ from rest_framework.generics import get_object_or_404
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
|
||||
from .models import NestedProgram, Job, ComputeResource
|
||||
from .permissions import IsOwner
|
||||
from .serializers import ProgramSerializer, JobSerializer
|
||||
|
|
|
@ -10,5 +10,6 @@ RUN rm -r ./qs
|
|||
|
||||
COPY --chown=$NB_UID:$NB_UID ./docs/tutorials/ ./serverless/tutorials/
|
||||
COPY --chown=$NB_UID:$NB_UID ./docs/guides/ ./serverless/guides/
|
||||
COPY --chown=$NB_UID:$NB_UID ./docs/getting_started/ ./serverless/getting_started/
|
||||
|
||||
ENV JUPYTER_ENABLE_LAB=no
|
||||
|
|
2191
realm-export.json
2191
realm-export.json
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue