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(opens in a new tab) (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(opens in a new tab).
This is an experimental feature, subject to change.
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:
- Map classical inputs to a quantum problem
- Optimize problem for quantum execution
- Execute using Qiskit Runtime primitives
- 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:
- Build the Qiskit pattern
- Deploy to the Qiskit Serverless
- 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.)
# 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(opens in a new tab) and handle external Python dependencies(opens in a new tab).
After creating a workflow, authenticate to the IBMServerlessClient
with your IBM Quantum token, which can be obtained from your IBM Quantum account(opens in a new tab), and upload the script.
# 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.
# 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(opens in a new tab).
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 QPUs. This illustrates how the selectors can be used within a four-step Qiskit pattern.
Instead of manually selecting a QPU, step 2 of the Qiskit pattern optimizes the circuits for execution by using the QPU selectors from Qiskit Serverless to automatically allocate a QPU according to desired criteria. Here, IBMLeastNoisyQPUSelector
finds the QPU, 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 QPU 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 QPU when determining the most optimal QPU and qubit layout. All IBMQPUSelector
s implement a get_backend
method, which retrieves the optimal QPU with respect to the given context. The get_backend
method also allows for additional filtering of the QPUs. It is implemented using the same interface as the QiskitRuntimeService.backends method.
Then, in step 3 of the pattern, you execute the target circuit on the QPU chosen by the selector.
# 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 QPU
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 QPU 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)
# 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 QPU
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 QPU 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)
After creating this pattern, you can deploy and run it remotely with Qiskit Serverless as described above.