Advanced Qiskit Runtime options
When calling the primitives, you can pass in options by using an options class or a dictionary. In the options classes, commonly used options, such as resilience_level
, are at the first level. Other options are grouped into different categories, such as execution
. See all the available options in the API reference.
To ensure faster and more efficient results, as of 1 March 2024, circuits and observables need to be transformed to only use instructions supported by the system (referred to as instruction set architecture (ISA) circuits and observables) before being submitted to the Qiskit Runtime primitives. See the transpilation documentation for instructions to transform circuits. Due to this change, the primitives will no longer perform layout or routing operations. Consequently, transpilation options referring to those tasks will no longer have any effect. By default, all primitives except Sampler V2 still optimize the input circuits. To bypass all optimization, set optimization_level=0
.
Exception: When you initialize the Qiskit Runtime Service with the Q-CTRL channel strategy (example below), abstract circuits are still supported.
service = QiskitRuntimeService(channel="ibm_cloud", channel_strategy="q-ctrl")
This section focuses on Qiskit Runtime primitive options. While most of the primitives
interface is common across implementations, most options are not. Consult the
corresponding API references for information about the qiskit.primitives
and qiskit_aer.primitives
options.
V2 changes
Options are specified differently in the V2 primitives in these ways:
SamplerV2
andEstimatorV2
now have separate options classes. You can see the available options and update option values during or after primitive initialization.- Instead of the
set_options()
method, V2 primitive options have theupdate()
method that applies changes to theoptions
attribute. - If you do not specify a value for an option, it is given a special value of
Unset
and the server defaults are used. - For V2 primitives, the
options
attribute is thedataclass
Python type. You can use the built-inasdict
method to convert it to a dictionary.
Instantiate the Options class (V1)
In the example below, we create an instance of the Options
class. optimization_level
is a first-level option and can be passed as an input parameter. Options related to the execution environment are passed using the environment
parameter.
from qiskit_ibm_runtime import Options
options = Options(optimization_level=1, environment={"log_level": "INFO"})
The Options
class supports auto-complete. Once you create an instance of the Options
class, you can use auto-complete to see what options are available. If you choose one of the categories, you can use auto-complete again to see what options are available under that category.
from qiskit_ibm_runtime import Options
options = Options()
options.resilience_level = 1
options.execution.shots = 2048
Pass options to a primitive
Options class
When creating an instance of the Estimator
or Sampler
class, you can pass in the options
you created in the options class. Those options will then be applied when you use run()
to perform the calculation. Example:
SamplerV2
and EstimatorV2
have separate options classes that do not need to be instantiated. You can see the available options and update option values during or after primitive initialization. Those options will then be applied when you use run()
to perform the calculation. Example:
estimator = Estimator(backend=backend)
# Setting options after primitive initialization
# This uses auto complete.
estimator.options.optimization_level = 1
estimator.options.default_shots = 4000
When creating an instance of the Estimator
or Sampler
class, you can pass in the options
you created in the options class. Those options will then be applied when you use run()
to perform the calculation. Example:
estimator = Estimator(session=backend, options=options)
result = estimator.run(circuit, observable).result()
print(f">>> Metadata: {result.metadata[0]}")
Primitive initialization
You can specify options when initializing the primitive.
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})
Run() method
You can pass in options by using the run()
method. This overwrites the options you specified when creating the Estimator
or Sampler
instance for that particular execution.
In V2, the only options you can pass to run()
are options defined in the interface. That is, shots
for Sampler and precision
for Estimator.
# Sample two circuits at 128 shots each.
sampler.run([(circuit1,), (circuit2,)], shots=128)
You can pass any options to run()
. Because most users will only overwrite a few options at the job level, it is not necessary to specify the category the options are in. The code below, for example, specifies shots=1024
instead of execution={"shots": 1024}
(which is also valid).
estimator = Estimator(session=backend, options=options)
result = estimator.run(circuit, observable, shots=1024).result()
print(f">>> Metadata: {result.metadata[0]}")
Commonly used options
There are many available options, but the following are the most commonly used:
Shots
For some algorithms, setting a specific number of shots is a core part of their routines. There are several ways to set and update shots with the primitives.
from qiskit_ibm_runtime import SamplerV2 as Sampler
# Setting shots during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})
# Setting options after primitive initialization
# This uses auto complete.
sampler.options.default_shots=2000
# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"})
# Sample two circuits at 128 shots each.
sampler.run([(circuit1,), (circuit2,)], shots=128)
Previously, shots could be set during the call to backend.run()
. For example, backend.run(shots=1024)
. Now, that setting is part of the execution
options ("second level option"). This can be done during the primitive setup:
from qiskit_ibm_runtime import Estimator, Options
options = Options()
options.execution.shots = 1024
estimator = Estimator(session=backend, options=options)
If you need to modify the number of shots set between iterations (primitive calls), you can set the
shots directly in the run()
method. This overwrites the initial shots
setting.
from qiskit_ibm_runtime import Estimator
estimator = Estimator(session=backend)
estimator.run(circuits=circuits, observables=observables, shots=50)
# other logic
estimator.run(circuits=circuits, observables=observables, shots=100)
For more information about the primitive options, refer to the Options class API reference.
Runtime compilation
The Qiskit Runtime primitives expect to be called with circuits already suitable for execution on the target system. This implies that the user has already transpiled their circuits to respect the native gate set and connectivity constraints of the target system.
The Qiskit Runtime primitives may perform additional runtime compilation to optimize circuits, with the degree of optimization controlled by an optimization level option. The optimization level you choose affects the compilation strategy, with higher levels invoking more expensive or aggressive optimizations.
See the Optimization level table in the Runtime compilation topic for further details.
The optimization level option is a "first-level option". Sampler V2 does not currently support optimization_level
.
from qiskit_ibm_runtime import EstimatorV2 as Estimator
estimator = Estimator(session=backend, options={"optimization_level": 1})
# or..
estimator.options.optimization_level = 1
To turn off all optional runtime compilation steps, you must set optimization_level=0
. V2 primitives do not support any advanced transpilation options.
In the currently deployed Qiskit Runtime primitives, optimization levels 2 and 3 behave identically to level 1. If you want to use more advanced optimization, use the Qiskit transpiler locally, set skip_transpilation=True
, and then pass the transpiled circuits to the primitives. For instructions see the Submit pre-transpiled circuits(opens in a new tab) tutorial.
The optimization level option is a "first-level option", and can be set as follows:
from qiskit_ibm_runtime import Estimator, Options
options = Options(optimization_level=1)
# or..
options = Options()
options.optimization_level = 1
estimator = Estimator(session=backend, options=options)
Turning off all optional runtime compilation steps requires a "second-level option", as follows:
from qiskit_ibm_runtime import Estimator, Options
options = Options()
options.transpilation.skip_transpilation = True
estimator = Estimator(session=backend, options=options)
For more information and a complete list of advanced transpilation options, see the Advanced transpilation options table in the Runtime compilation topic.
Error mitigation
You might want to leverage different error mitigation methods and see how these affect the performance of your
algorithm. These methods can be set through the resilience_level
option. For more information about error mitigation, see the
Configure error mitigation topic.
With Estimator V2, you can set resilience levels 0-2 and you can also turn on and off various error mitigation settings for fine tuning.
estimator = Estimator(backend=backend)
estimator.options.resilience_level = 2
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = [1, 3, 5]
The method selected for each level is
different for Sampler
and Estimator
.
The configuration is similar to the other options:
from qiskit_ibm_runtime import Estimator, Options
options = Options(resilience_level = 2)
# or...
options = Options()
options.resilience_level = 2
estimator = Estimator(session=backend, options=options)
Next steps
- Find more details about the
Estimator
methods in the Estimator API reference. - Find more details about the
Sampler
methods in the Sampler API reference. - Find all available options in the Options API reference.
- Find details about runtime compilation and error mitigation.
- Learn how to transpile locally in the Transpile section.
- Try the Submit pre-transpiled circuits(opens in a new tab) tutorial.