233 lines
10 KiB
Plaintext
233 lines
10 KiB
Plaintext
---
|
|
title: Run workloads remotely with Qiskit Serverless
|
|
description: How to run quantum computing workloads remotely using Qiskit Serverless on IBM Quantum.
|
|
---
|
|
|
|
# Run workloads remotely with Qiskit Serverless
|
|
|
|
Premium users can build, deploy, and run their workloads remotely on classical compute made available through the IBM Quantum™ Platform.
|
|
|
|
Try out the tutorials in [IBM Quantum Learning](https://learning.quantum.ibm.com/catalog/tutorials?topics=qiskit-patterns) (note: these are accessible in the Premium Plan once you have logged into your IBM Quantum account) and explore more of the features of Qiskit Serverless in the [documentation](https://qiskit.github.io/qiskit-serverless/).
|
|
|
|
|
|
<Admonition type="note">
|
|
This is an experimental feature, subject to change.
|
|
</Admonition>
|
|
|
|
## Building Qiskit patterns with Qiskit Serverless
|
|
|
|
Creating utility-scale quantum applications generally requires a variety of compute resource requirements. You can use Qiskit Serverless to easily submit quantum workflows for remote, managed execution on IBM Quantum Platform. These quantum workflows can typically be implemented within a common pattern, called a Qiskit pattern. A Qiskit pattern is an intuitive, repeatable set of steps for implementing a quantum computing workflow.
|
|
|
|
Steps in a Qiskit pattern:
|
|
|
|
1. Map classical inputs to a quantum problem
|
|
2. Optimize problem for quantum execution
|
|
3. Execute using Qiskit Runtime primitives
|
|
4. Post-process, return result in classical format
|
|
|
|
Once you have built a Qiskit pattern, you can use Qiskit Serverless to deploy it and submit it for managed execution. Overall, the process of creating quantum software and submitting it for managed execution on a remote cluster can be broken down into three steps:
|
|
|
|
1. Build the Qiskit pattern
|
|
2. Deploy to the Qiskit Serverless
|
|
3. Run remotely on Qiskit Serverless
|
|
|
|
## Build a Qiskit pattern
|
|
|
|
Here is an example of computing the expectation value using the Qiskit Runtime Estimator primitive. This Python script should be saved in your working directory. (Warning! All contents of the working directory will be shipped to the cluster for execution.)
|
|
|
|
|
|
```python
|
|
# source_files/my_qiskit_pattern.py
|
|
|
|
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
|
from qiskit.circuit.random import random_circuit
|
|
from qiskit.quantum_info import SparsePauliOp
|
|
from qiskit_ibm_runtime import QiskitRuntimeService
|
|
from qiskit_serverless import save_result
|
|
|
|
service = QiskitRuntimeService()
|
|
backend = service.least_busy(simulator=False)
|
|
|
|
# Step 1: Map quantum circuits and operators
|
|
abstract_circuit = random_circuit(2, 2, seed=1234)
|
|
observable = SparsePauliOp("IY")
|
|
|
|
# Step 2: Optimize the circuit for quantum execution
|
|
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
|
|
target_circuit = pm.run(abstract_circuit)
|
|
target_observable = observable.apply_layout(target_circuit.layout)
|
|
|
|
# Step 3: Execute the target circuit on Estimator V1
|
|
# from qiskit_ibm_runtime import Estimator
|
|
# estimator = Estimator(backend)
|
|
# job = estimator.run(target_circuit, target_observable)
|
|
# result = job.result()
|
|
|
|
# Step 3: Execute the target circuit on Estimator V2
|
|
from qiskit_ibm_runtime import EstimatorV2 as Estimator
|
|
estimator = Estimator(backend)
|
|
job = estimator.run([(target_circuit, target_observable)])
|
|
result = job.result()
|
|
|
|
|
|
# Step 4: Postprocess the Estimator V1 results
|
|
# print(result)
|
|
# save results of program execution
|
|
# note: saved items must be serializable
|
|
# save_result(result.values)
|
|
|
|
# Step 4: Postprocess the Estimator V2 results
|
|
print(result)
|
|
# save results of program execution
|
|
# note: saved items must be serializable
|
|
save_result(result[0].data.evs)
|
|
```
|
|
|
|
Please refer to our guides on how to configure your pattern to [accept input arguments](https://qiskit.github.io/qiskit-serverless/getting_started/basic/02_arguments_and_results.html) and [handle external Python dependencies](https://qiskit.github.io/qiskit-serverless/getting_started/basic/03_dependencies.html).
|
|
|
|
After creating a workflow, authenticate to the `IBMServerlessClient` with your IBM Quantum token, which can be obtained from your [IBM Quantum account](https://quantum.ibm.com/account), and upload the script.
|
|
|
|
```python
|
|
# Authenticate to the IBM serverless client
|
|
from qiskit_serverless import IBMServerlessClient
|
|
serverless = IBMServerlessClient("YOUR_IBM_QUANTUM_TOKEN")
|
|
|
|
# Deploy the pattern
|
|
from qiskit_serverless import QiskitFunction
|
|
serverless.upload(
|
|
QiskitFunction(
|
|
title="My-Qiskit-Pattern",
|
|
entrypoint="my_qiskit_pattern.py",
|
|
working_dir="./source_files/"
|
|
)
|
|
)
|
|
```
|
|
|
|
## Run a Qiskit pattern remotely on Qiskit Serverless
|
|
|
|
Finally, the pattern is ready to run remotely.
|
|
|
|
```python
|
|
# Run pattern remotely
|
|
pattern = serverless.get("My-Qiskit-Pattern")
|
|
pattern.run()
|
|
|
|
# Retrieve status, logs, results
|
|
job.status()
|
|
job.logs()
|
|
job.result()
|
|
```
|
|
|
|
## Migration guide
|
|
|
|
Qiskit Runtime custom programs can be easily migrated to Qiskit Serverless via this [migration guide](https://qiskit.github.io/qiskit-serverless/migration/migration_from_qiskit_runtime_programs.html).
|
|
|
|
## Resource management (alpha)
|
|
|
|
Premium Plan users have access to an alpha release of resource management functionality through Qiskit Serverless. This enables automatic selection of quantum hardware for your workloads.
|
|
|
|
The example below demonstrates how to use `IBMQPUSelector` to automate the process of selecting which qubits will be used from a set of available systems. This illustrates how the selectors can be used within a four-step Qiskit pattern.
|
|
|
|
Instead of manually selecting a system, step 2 of the Qiskit pattern optimizes the circuits for execution by using the QPU selectors from Qiskit Serverless to automatically allocate a system according to desired criteria. Here, `IBMLeastNoisyQPUSelector` finds the system, among the ones available to you through your IBM Quantum account, that yields the least-noisy qubit subgraph for the input circuit. You can also use the `IBMLeastBusyQPUSelector` to find a system that can support the circuit width but with the shortest queue.
|
|
|
|
For each `IBMQPUSelector`, the context is set in the constructor. All `IBMQPUSelectors` require Qiskit Runtime credentials. The `IBMLeastNoisyQPUSelector` requires a circuit and transpile options specifying how the circuit should be optimized for each system when determining the most optimal QPU and qubit layout. All `IBMQPUSelector`s implement a `get_backend` method, which retrieves the optimal system with respect to the given context. The `get_backend` method also allows for additional filtering of the systems. It is implemented using the same interface as the [QiskitRuntimeService.backends method](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.QiskitRuntimeService#backends).
|
|
|
|
Then, in step 3 of the pattern, you execute the target circuit on the system chosen by the selector.
|
|
|
|
<Tabs>
|
|
<TabItem value="SamplerV2" label="Sampler V2">
|
|
```python
|
|
# source_files/my_qiskit_pattern_resource_management.py
|
|
|
|
from qiskit_ibm_runtime import QiskitRuntimeService, Session, Samplerv2 as Sampler
|
|
from qiskit.circuit.random import random_circuit
|
|
from qiskit_serverless_tools.selectors import IBMLeastNoisyQPUSelector
|
|
|
|
service = QiskitRuntimeService()
|
|
|
|
# Step 1: Map quantum circuits and operators
|
|
abstract_circuit = random_circuit(
|
|
num_qubits=5, depth=4, measure=True, seed=1234
|
|
)
|
|
|
|
# Step 2: Optimize the circuit for quantum execution with automatically selected system
|
|
selector = IBMLeastNoisyQPUSelector(
|
|
service, circuit=abstract_circuit, transpile_options={"optimization_level": 3}
|
|
)
|
|
backend = selector.get_backend(min_num_qubits=127)
|
|
target_circuit = selector.optimized_circuit
|
|
|
|
## Alternatively, one can automatically select a system according to most available:
|
|
# from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
|
# from qiskit_serverless_tools.selectors import IBMLeastBusyQPUSelector
|
|
#
|
|
# backend = IBMLeastBusyQPUSelector(service).get_backend(min_num_qubits=127)
|
|
# pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
|
|
# target_circuit = pm.run(abstract_circuit)
|
|
|
|
# Step 3: Execute the target circuit
|
|
sampler = Sampler(mode=backend, options={"default_shots": 1024})
|
|
job = sampler.run([target_circuit])
|
|
result = job.result()
|
|
|
|
# Step 4: Postprocess the results
|
|
print(f" > Counts: {result[0].data.meas.get_counts()}")
|
|
|
|
# save results of program execution
|
|
# note: saved items must be serializable
|
|
save_result(result)
|
|
```
|
|
</TabItem>
|
|
|
|
<TabItem value="SamplerV1" label="Sampler (V1)">
|
|
```python
|
|
# source_files/my_qiskit_pattern_resource_management.py
|
|
|
|
from qiskit_ibm_runtime import QiskitRuntimeService, Session, Sampler, Options
|
|
from qiskit.circuit.random import random_circuit
|
|
from qiskit_serverless_tools.selectors import IBMLeastNoisyQPUSelector
|
|
|
|
service = QiskitRuntimeService()
|
|
|
|
# Step 1: Map quantum circuits and operators
|
|
abstract_circuit = random_circuit(
|
|
num_qubits=5, depth=4, measure=True, seed=1234
|
|
)
|
|
|
|
# Step 2: Optimize the circuit for quantum execution with automatically selected system
|
|
selector = IBMLeastNoisyQPUSelector(
|
|
service, circuit=abstract_circuit, transpile_options={"optimization_level": 3}
|
|
)
|
|
backend = selector.get_backend(min_num_qubits=127)
|
|
target_circuit = selector.optimized_circuit
|
|
|
|
## Alternatively, one can automatically select a system according to most available:
|
|
# from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
|
# from qiskit_serverless_tools.selectors import IBMLeastBusyQPUSelector
|
|
#
|
|
# backend = IBMLeastBusyQPUSelector(service).get_backend(min_num_qubits=127)
|
|
# pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
|
|
# target_circuit = pm.run(abstract_circuit)
|
|
|
|
# Step 3: Execute the target circuit
|
|
with Session(service, backend=backend) as session:
|
|
sampler = Sampler(
|
|
options=Options(
|
|
execution={"shots": 1024}, transpilation={"skip_transpilation": True}
|
|
)
|
|
)
|
|
result = sampler.run(target_circuit).result().quasi_dists[0]
|
|
|
|
# Step 4: Postprocess the results
|
|
print(result)
|
|
|
|
# save results of program execution
|
|
# note: saved items must be serializable
|
|
save_result(result)
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
After creating this pattern, you can deploy and run it remotely with Qiskit Serverless as described above.
|