1180 lines
42 KiB
Plaintext
1180 lines
42 KiB
Plaintext
---
|
||
title: Migrate to the V2 primitives
|
||
description: Migrate from using the original primitives (Estimator and Sampler) to using the updated V2 primitives (Estimator V2 and Sampler V2)
|
||
|
||
---
|
||
|
||
{/* cspell:ignore expvals */}
|
||
|
||
<span id="v2-primitives"></span>
|
||
# Migrate to the Qiskit Runtime V2 primitives
|
||
|
||
<Admonition type="warning">
|
||
The original primitives (referred to as the V1 primitives), [V1 Sampler](/api/qiskit-ibm-runtime/0.26/qiskit_ibm_runtime.SamplerV1) and [V1 Estimator](/api/qiskit-ibm-runtime/0.26/qiskit_ibm_runtime.EstimatorV1), have been deprecated in `qiskit-ibm-runtime` 0.23.
|
||
Their support was removed on 15 August 2024.
|
||
</Admonition>
|
||
|
||
With the deprecation of the V1 primitives, all code should be migrated to use the V2 interfaces. This guide describes what changed in the Qiskit Runtime V2 primitives (available with qiskit-ibm-runtime 0.21.0) and why, describes each new primitive in detail, and gives examples to help you migrate code from using the legacy primitives to the V2 primitives. The examples in the guide all use the Qiskit Runtime primitives, but in general, the same changes apply to the other primitive implementations. The functions unique to Qiskit Runtime such as error mitigation remain unique to Qiskit Runtime.
|
||
|
||
For information about the changes to the Qiskit reference primitives (now called *statevector* primitives), see [the qiskit.primitives section](qiskit-1.0-features#qiskit.primitives) in the Qiskit 1.0 feature changes page. See [StatevectorSampler](/api/qiskit/qiskit.primitives.StatevectorSampler) and [StatevectorEstimator](/api/qiskit/qiskit.primitives.StatevectorEstimator) for V2 primitive reference implementations.
|
||
|
||
## Overview
|
||
|
||
Version 2 of the primitives is introduced with a new base class for both Sampler and Estimator ([BaseSamplerV2](/api/qiskit/qiskit.primitives.BaseSamplerV2) and [BaseEstimatorV2](/api/qiskit/qiskit.primitives.BaseEstimatorV2)), along with new types for their inputs and outputs.
|
||
|
||
The new interface lets you specify a single circuit and multiple observables (if using Estimator) and parameter value sets for that circuit, so that sweeps over parameter value sets and observables can be efficiently specified. Previously, you had to specify the same circuit multiple times to match the size of the data to be combined. Also, while you can still use `resilience_level` (if using Estimator) as the simple knob, V2 primitives give you the flexibility to turn on or off individual error mitigation / suppression methods to customize them for your needs.
|
||
|
||
To reduce the total job execution time, V2 primitives only accept circuits and observables that use instructions supported by the target QPU (quantum processing unit). Such circuits and observables are referred to as instruction set architecture (ISA) circuits and observables. V2 primitives do not perform layout, routing, and translation operations. See the [transpilation documentation](/guides/transpile) for instructions to transform circuits.
|
||
|
||
Sampler V2 is simplified to focus on its core task of sampling the output register from execution of quantum circuits. It returns the samples, whose type is defined by the program, without weights. The output data is also separated by the output register names defined by the program. This change enables future support for circuits with classical control flow.
|
||
|
||
See the [EstimatorV2 API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.EstimatorV2) and [SamplerV2 API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.SamplerV2) for full details.
|
||
|
||
## Major changes
|
||
|
||
### Import
|
||
|
||
For backward compatibility, you must explicity import the V2 primitives. Specifying `import <primitive>V2 as <primitive>` is not required, but makes it easier to transition code to V2.
|
||
|
||
<Admonition type="warning">
|
||
After the V1 primitives are no longer supported, `import <primitive>` will import the V2 version of the specified primitive.
|
||
</Admonition>
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
from qiskit_ibm_runtime import EstimatorV2 as Estimator
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
from qiskit_ibm_runtime import Estimator
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
```python
|
||
from qiskit_ibm_runtime import SamplerV2 as Sampler
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
from qiskit_ibm_runtime import Sampler
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
|
||
### Input and output
|
||
|
||
#### Input
|
||
|
||
Both `SamplerV2` and `EstimatorV2` take one or more *primitive unified blocs* (PUBs) as the input. Each PUB is a tuple that contains **one** circuit and the data broadcasted to that circuit, which can be multiple observables and parameters. Each PUB returns a result.
|
||
|
||
* Sampler V2 PUB format: (`<circuit>`, `<parameter values>`, `<shots>`), where `<parameter values>`and `<shots>` are optional.
|
||
* Estimator V2 PUB format: (`<circuit>`, `<observables>`, `<parameter values>`, `<precision>`), where `<parameter values>`and `<precision>` are optional.
|
||
Numpy [broadcasting rules](https://numpy.org/doc/stable/user/basics.broadcasting.html) are used when combining observables and parameter values.
|
||
|
||
Additionally, the following changes have been made:
|
||
|
||
* Estimator V2 has gained a `precision` argument in the `run()` method that specifies the targeted precision of the expectation value estimates.
|
||
* Sampler V2 has the `shots` argument in its `run()` method.
|
||
|
||
##### Examples
|
||
|
||
Estimator V2 example that uses precision in `run()`:
|
||
|
||
```python
|
||
# Estimate expectation values for two PUBs, both with 0.05 precision.
|
||
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)
|
||
```
|
||
|
||
Sampler V2 example that uses shots in `run()`:
|
||
|
||
```python
|
||
# Sample two circuits at 128 shots each.
|
||
sampler.run([circuit1, circuit2], shots=128)
|
||
|
||
# Sample two circuits at different amounts of shots.
|
||
# The "None"s are necessary as placeholders
|
||
# for the lack of parameter values in this example.
|
||
sampler.run([
|
||
(circuit1, None, 123),
|
||
(circuit2, None, 456),
|
||
])
|
||
```
|
||
|
||
#### Output
|
||
|
||
The output is now in the [`PubResult`](/api/qiskit/qiskit.primitives.PubResult) format. A `PubResult` is the data and metadata resulting from a single PUB’s execution.
|
||
|
||
* Estimator V2 continues to return expectation values.
|
||
* The `data` portion of a Estimator V2 PubResult contains both expectation values and standard errors (`stds`). V1 returned variance in metadata.
|
||
* Sampler V2 returns per-shot measurements in the form of **bitstrings**,
|
||
instead of the quasi-probability distributions from the V1
|
||
interface. The bitstrings show the measurement outcomes, preserving the shot
|
||
order in which they were measured.
|
||
* Sampler V2 has convenience methods like `get_counts()` to help with migration.
|
||
* The Sampler V2 result objects organize data in terms of their **input circuits' classical register names**, for
|
||
compatibility with dynamic circuits. By default, the classical register name is `meas`, as shown in the following example. When defining your circuit, if you create one or more classical registers with a non-default name, use that name to get the results. You can find the classical register name by running `<circuit_name>.cregs`. For example, `qc.cregs`.
|
||
|
||
```python
|
||
# Define a quantum circuit with 2 qubits
|
||
circuit = QuantumCircuit(2)
|
||
circuit.h(0)
|
||
circuit.cx(0, 1)
|
||
circuit.measure_all()
|
||
circuit.draw()
|
||
```
|
||
```text
|
||
┌───┐ ░ ┌─┐
|
||
q_0: ┤ H ├──■───░─┤M├───
|
||
└───┘┌─┴─┐ ░ └╥┘┌─┐
|
||
q_1: ─────┤ X ├─░──╫─┤M├
|
||
└───┘ ░ ║ └╥┘
|
||
meas: 2/══════════════╩══╩═
|
||
0 1
|
||
```
|
||
|
||
#### Estimator examples (input and output)
|
||
|
||
{/*Verified by Elena.*/}
|
||
|
||
<Tabs>
|
||
<TabItem value="Estimator14" label="1 circuit, 4 observables">
|
||
```python
|
||
# Estimator V1: Execute 1 circuit with 4 observables
|
||
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
|
||
evs = job.result().values
|
||
|
||
# Estimator V2: Execute 1 circuit with 4 observables
|
||
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
|
||
evs = job.result()[0].data.evs
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="Estimator142" label="1 circuit, 4 observables, 2 parameter sets">
|
||
```python
|
||
# Estimator V1: Execute 1 circuit with 4 observables and 2 parameter sets
|
||
job = estimator_v1.run([circuit] * 8, [obs1, obs2, obs3, obs4] * 2, [vals1, vals2] * 4)
|
||
evs = job.result().values
|
||
|
||
# Estimator V2: Execute 1 circuit with 4 observables and 2 parameter sets
|
||
|
||
job = estimator_v2.run([(circuit, [[obs1], [obs2], [obs3], [obs4]], [[vals1], [vals2]])])
|
||
evs = job.result()[0].data.evs
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="Estimator22" label="2 circuits, 2 observables">
|
||
```python
|
||
# Estimator V1: Cannot execute 2 circuits with different observables
|
||
|
||
# Estimator V2: Execute 2 circuits with 2 different observables. There are
|
||
# two PUBs because each PUB can have only one circuit.
|
||
job = estimator_v2.run([(circuit1, obs1), (circuit2, obs2)])
|
||
evs1 = job.result()[0].data.evs # result for pub 1 (circuit 1)
|
||
evs2 = job.result()[1].data.evs # result for pub 2 (circuit 2)
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
#### Sampler examples (input and output)
|
||
|
||
{/*Verified by Elena.*/}
|
||
|
||
<Tabs>
|
||
<TabItem value="Sampler13" label="1 circuit, 3 parameter sets">
|
||
```python
|
||
# Sampler V1: Execute 1 circuit with 3 parameter sets
|
||
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
|
||
dists = job.result().quasi_dists
|
||
|
||
# Sampler V2: Executing 1 circuit with 3 parameter sets
|
||
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
|
||
counts = job.result()[0].data.meas.get_counts()
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="Sampler21" label="2 circuits, 1 parameter set">
|
||
```python
|
||
# Sampler V1: Execute 2 circuits with 1 parameter set
|
||
job = sampler_v1.run([circuit1, circuit2], [vals1] * 2)
|
||
dists = job.result().quasi_dists
|
||
|
||
# Sampler V2: Execute 2 circuits with 1 parameter set
|
||
job = sampler_v2.run([(circuit1, vals1), (circuit2, vals1)])
|
||
counts1 = job.result()[0].data.meas.get_counts() # result for pub 1 (circuit 1)
|
||
counts2 = job.result()[1].data.meas.get_counts() # result for pub 2 (circuit 2)
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
Example that uses different output registers
|
||
|
||
```python
|
||
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
|
||
|
||
alpha = ClassicalRegister(5, "alpha")
|
||
beta = ClassicalRegister(7, "beta")
|
||
qreg = QuantumRegister(12)
|
||
|
||
circuit = QuantumCircuit(qreg, alpha, beta)
|
||
circuit.h(0)
|
||
circuit.measure(qreg[:5], alpha)
|
||
circuit.measure(qreg[5:], beta)
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=12)
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run([isa_circuit])
|
||
result = job.result()
|
||
# Get results for the first (and only) PUB
|
||
pub_result = result[0]
|
||
print(f" >> Counts for the alpha output register: {pub_result.data.alpha.get_counts()}")
|
||
print(f" >> Counts for the beta output register: {pub_result.data.beta.get_counts()}")
|
||
```
|
||
|
||
<span id="v2-prim-options"></span>
|
||
### Options
|
||
|
||
Options are specified differently in the V2 primitives in these ways:
|
||
|
||
- `SamplerV2` and `EstimatorV2` 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 the `update()` method that applies changes to the `options` 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 the `dataclass` Python type. You can use the built-in `asdict` method to convert it to a dictionary.
|
||
|
||
See the [API reference](/api/qiskit-ibm-runtime/options) for the list of available options.
|
||
|
||
{/*Verified EstimatorV2. 2/23 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
from dataclasses import asdict
|
||
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})
|
||
|
||
# Setting options after primitive initialization
|
||
# This uses auto complete.
|
||
estimator.options.default_shots = 4000
|
||
# This does bulk update.
|
||
estimator.options.update(default_shots=4000, resilience_level=2)
|
||
|
||
# Print the dictionary format.
|
||
# Server defaults are used for unset options.
|
||
print(asdict(estimator.options))
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False)
|
||
|
||
# Setting options during primitive initialization
|
||
options = Options()
|
||
# This uses auto complete.
|
||
options.resilience_level = 2
|
||
estimator = Estimator(backend=backend, options=options)
|
||
|
||
# Setting options after primitive initialization.
|
||
# This does bulk update.
|
||
estimator.set_options(shots=4000)
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
```python
|
||
from dataclasses import asdict
|
||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||
from qiskit_ibm_runtime import SamplerV2 as Sampler
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False)
|
||
|
||
# Setting options during primitive initialization
|
||
sampler = Sampler(backend, options={"default_shots": 4096})
|
||
|
||
# Setting options after primitive initialization
|
||
# This uses auto complete.
|
||
sampler.options.dynamical_decoupling.enable = True
|
||
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
|
||
sampler.options.twirling.enable_gates = True
|
||
|
||
# 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"})
|
||
|
||
# Print the dictionary format.
|
||
# Server defaults are used for unset options.
|
||
print(asdict(sampler.options))
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False)
|
||
|
||
# Setting options during primitive initialization
|
||
options = Options()
|
||
# This uses auto complete.
|
||
options.resilience_level = 2
|
||
sampler = Sampler(backend=backend, options=options)
|
||
|
||
# Setting options after primitive initialization.
|
||
# This does bulk update.
|
||
sampler.set_options(shots=2000)
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
<span id="error-mit"></span>
|
||
### Error mitigation and suppression
|
||
|
||
* Because Sampler V2 returns samples without postprocessing, it does not support resilience levels.
|
||
* Sampler V2 does not support `optimization_level`.
|
||
* Estimator V2 will drop support for `optimization_level` on or around 30 September 2024.
|
||
* Estimator V2 does not support resilience level 3. This is because resilience level 3 in V1 Estimator uses Probabilistic Error Cancellation (PEC), which is proven to give unbiased results at the cost of exponential processing time. Level 3 was removed to draw attention to that tradeoff. You can, however, still use PEC as the error mitigation method by specifying the `pec_mitigation` option.
|
||
* Estimator V2 supports `resilience_level` 0-2, as described in the following table. These options are more advanced than their V1 counterparts. You can also explicitly turn on / off individual error mitigation / suppression methods.
|
||
|
||
| Level 1 | Level 2 |
|
||
|---------------------------|---------------------------|
|
||
| Measurement twirling | Measurement twirling |
|
||
| Readout error mitigation | Readout error mitigation |
|
||
| | ZNE |
|
||
|
||
{/*Verified EstimatorV2. 2/23 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
from dataclasses import asdict
|
||
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)
|
||
|
||
# Set resilience_level to 0
|
||
estimator.options.resilience_level = 0
|
||
|
||
# Turn on measurement error mitigation
|
||
estimator.options.resilience.measure_mitigation = True
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
from qiskit_ibm_runtime import Estimator, Options
|
||
|
||
estimator = Estimator(backend, options=options)
|
||
|
||
options = Options()
|
||
|
||
options.resilience_level = 2
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
|
||
```python
|
||
from qiskit_ibm_runtime import SamplerV2 as Sampler
|
||
|
||
sampler = Sampler(backend)
|
||
# Turn on dynamical decoupling with sequence XpXm.
|
||
sampler.options.dynamical_decoupling.enable = True
|
||
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
|
||
|
||
print(f">> dynamical decoupling sequence to use: {sampler.options.dynamical_decoupling.sequence_type}")
|
||
```
|
||
</TabItem>
|
||
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
from qiskit_ibm_runtime import Sampler, Options
|
||
|
||
sampler = Sampler(backend, options=options)
|
||
|
||
options = Options()
|
||
|
||
options.resilience_level = 2
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
<span id="transpilation"></span>
|
||
### Transpilation
|
||
|
||
V2 primitives support only circuits that adhere to the Instruction Set Architecture (ISA) of a particular backend. Because the primitives do not perform layout, routing, and translation operations, the corresponding transpilation options from V1 are not supported.
|
||
|
||
|
||
### Job status
|
||
|
||
The V2 primitives have a new `RuntimeJobV2` class, which inherits from `BasePrimitiveJob`. The `status()` method of this new class returns a string instead of a JobStatus enum from Qiskit. See the [RuntimeJobV2 API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.RuntimeJobV2) for details.
|
||
|
||
<Tabs>
|
||
<TabItem value="V2" label="V2 primitives">
|
||
```python
|
||
job = estimator.run(...)
|
||
|
||
# check if a job is still running
|
||
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="V1" label="V1 primitives">
|
||
```python
|
||
from qiskit.providers.jobstatus import JobStatus
|
||
|
||
job = estimator.run(...)
|
||
|
||
#check if a job is still running
|
||
print(f"Job {job.job_id()} is still running: {job.status() is JobStatus.RUNNING}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
## Steps to migrate to Estimator V2
|
||
|
||
1. Replace `from qiskit_ibm_runtime import Estimator` with `from qiskit_ibm_runtime import EstimatorV2 as Estimator`.
|
||
2. Remove any `from qiskit_ibm_runtime import Options` statements, since the `Options` class is not used by V2 primitives. You can instead pass options as a dictionary when initializing the `EstimatorV2` class (for example `estimator = Estimator(backend, options={“dynamical_decoupling”: {“enable”: True}})`), or set them after initialization:
|
||
```python
|
||
estimator = Estimator(backend)
|
||
estimator.options.dynamical_decoupling.enable = True
|
||
```
|
||
|
||
3. Review all the [supported options](/api/qiskit-ibm-runtime/options) and make updates accordingly.
|
||
4. Group each circuit you want to run with the observables and parameter values you want to apply to the circuit in a tuple (a PUB). For example, use `(circuit1, observable1, parameter_set1)` if you want to run `circuit1` with `observable1` and `parameter_set1`.
|
||
|
||
1. You might need to reshape your arrays of observables or parameter sets if you want to apply their outer product. For example, an array of observables of shape (4, 1) and an array of parameter sets of shape (1, 6) will give you a result of (4, 6) expectation values. See the [Numpy broadcasting rules](https://numpy.org/doc/stable/user/basics.broadcasting.html) for more details.
|
||
2. You can optionally specify the precision you want for that specific PUB.
|
||
5. Update the estimator `run()` method to pass in the list of PUBs. For example, `run([(circuit1, observable1, parameter_set1)])`.
|
||
You can optionally specify a `precision` here, which would apply to all PUBs.
|
||
6. Estimator V2 job results are grouped by PUBs. You can see the expectation value and standard error for each PUB by indexing to it. For example:
|
||
```python
|
||
pub_result = job.result()[0]
|
||
print(f">>> Expectation values: {pub_result.data.evs}")
|
||
print(f">>> Standard errors: {pub_result.data.stds}")
|
||
```
|
||
|
||
## Estimator full examples
|
||
|
||
### Run a single experiment
|
||
|
||
Use Estimator to determine the expectation value of a single circuit-observable pair.
|
||
|
||
{/*Verified EstimatorV2. 2/23 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import SparsePauliOp, random_hermitian
|
||
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
estimator = Estimator(backend)
|
||
|
||
n_qubits = 127
|
||
|
||
mat = np.real(random_hermitian(n_qubits, seed=1234))
|
||
circuit = IQP(mat)
|
||
observable = SparsePauliOp("Z" * n_qubits)
|
||
|
||
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
|
||
isa_circuit = pm.run(circuit)
|
||
isa_observable = observable.apply_layout(isa_circuit.layout)
|
||
|
||
job = estimator.run([(isa_circuit, isa_observable)])
|
||
result = job.result()
|
||
|
||
print(f" > Expectation value: {result[0].data.evs}")
|
||
print(f" > Metadata: {result[0].metadata}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import SparsePauliOp, random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
n_qubits = 127
|
||
|
||
mat = np.real(random_hermitian(n_qubits, seed=1234))
|
||
circuit = IQP(mat)
|
||
observable = SparsePauliOp("Z" * n_qubits)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
isa_observable = observable.apply_layout(isa_circuit.layout)
|
||
|
||
estimator = Estimator(backend)
|
||
job = estimator.run(isa_circuit, isa_observable)
|
||
result = job.result()
|
||
|
||
print(f" > Observable: {observable.paulis}")
|
||
print(f" > Expectation value: {result.values}")
|
||
print(f" > Metadata: {result.metadata}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Run multiple experiments in a single job
|
||
|
||
Use Estimator to determine the expectation values of multiple circuit-observable pairs.
|
||
|
||
{/*Verified EstimatorV2. 2/23 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
|
||
n_qubits = 3
|
||
rng = np.random.default_rng()
|
||
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
|
||
circuits = [IQP(mat) for mat in mats]
|
||
observables = [
|
||
SparsePauliOp("X" * n_qubits),
|
||
SparsePauliOp("Y" * n_qubits),
|
||
SparsePauliOp("Z" * n_qubits),
|
||
]
|
||
|
||
isa_circuits = pm.run(circuits)
|
||
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]
|
||
|
||
|
||
estimator = Estimator(backend)
|
||
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1], isa_observables[1]),(isa_circuits[2], isa_observables[2])])
|
||
job_result = job.result()
|
||
for idx in range(len(job_result)):
|
||
pub_result = job_result[idx]
|
||
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
|
||
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import SparsePauliOp, random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng()
|
||
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
|
||
circuits = [IQP(mat) for mat in mats]
|
||
observables = [
|
||
SparsePauliOp("X" * n_qubits),
|
||
SparsePauliOp("Y" * n_qubits),
|
||
SparsePauliOp("Z" * n_qubits),
|
||
]
|
||
|
||
# Get ISA circuits
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuits = pm.run(circuits)
|
||
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]
|
||
|
||
estimator = Estimator(backend)
|
||
job = estimator.run(isa_circuits, isa_observables)
|
||
result = job.result()
|
||
|
||
print(f" > Expectation values: {result.values}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Run parameterized circuits
|
||
|
||
Use Estimator to run multiple experiments in a single job, leveraging parameter values to increase circuit reusability. In the following example, notice that steps 1 and 2 are the same for V1 and V2.
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
import numpy as np
|
||
|
||
from qiskit.circuit import QuantumCircuit, Parameter
|
||
from qiskit.quantum_info import SparsePauliOp
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||
|
||
# Step 1: Map classical inputs to a quantum problem
|
||
|
||
theta = Parameter("θ")
|
||
|
||
chsh_circuit = QuantumCircuit(2)
|
||
chsh_circuit.h(0)
|
||
chsh_circuit.cx(0, 1)
|
||
chsh_circuit.ry(theta, 0)
|
||
|
||
number_of_phases = 21
|
||
phases = np.linspace(0, 2 * np.pi, number_of_phases)
|
||
individual_phases = [[ph] for ph in phases]
|
||
|
||
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
|
||
ZX = SparsePauliOp.from_list([("ZX", 1)])
|
||
XZ = SparsePauliOp.from_list([("XZ", 1)])
|
||
XX = SparsePauliOp.from_list([("XX", 1)])
|
||
ops = [ZZ, ZX, XZ, XX]
|
||
|
||
# Step 2: Optimize problem for quantum execution.
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
chsh_isa_circuit = pm.run(chsh_circuit)
|
||
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]
|
||
|
||
from qiskit_ibm_runtime import EstimatorV2 as Estimator
|
||
|
||
# Step 3: Execute using Qiskit primitives.
|
||
|
||
# Reshape observable array for broadcasting
|
||
reshaped_ops = np.fromiter(isa_observables, dtype=object)
|
||
reshaped_ops = reshaped_ops.reshape((4, 1))
|
||
|
||
estimator = Estimator(backend, options={"default_shots": int(1e4)})
|
||
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
|
||
# Get results for the first (and only) PUB
|
||
pub_result = job.result()[0]
|
||
print(f">>> Expectation values: {pub_result.data.evs}")
|
||
print(f">>> Standard errors: {pub_result.data.stds}")
|
||
print(f">>> Metadata: {pub_result.metadata}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
import numpy as np
|
||
|
||
from qiskit.circuit import QuantumCircuit, Parameter
|
||
from qiskit.quantum_info import SparsePauliOp
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||
|
||
# Step 1: Map classical inputs to a quantum problem
|
||
|
||
theta = Parameter("θ")
|
||
|
||
chsh_circuit = QuantumCircuit(2)
|
||
chsh_circuit.h(0)
|
||
chsh_circuit.cx(0, 1)
|
||
chsh_circuit.ry(theta, 0)
|
||
|
||
number_of_phases = 21
|
||
phases = np.linspace(0, 2 * np.pi, number_of_phases)
|
||
individual_phases = [[ph] for ph in phases]
|
||
|
||
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
|
||
ZX = SparsePauliOp.from_list([("ZX", 1)])
|
||
XZ = SparsePauliOp.from_list([("XZ", 1)])
|
||
XX = SparsePauliOp.from_list([("XX", 1)])
|
||
ops = [ZZ, ZX, XZ, XX]
|
||
|
||
# Step 2: Optimize problem for quantum execution.
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
chsh_isa_circuit = pm.run(chsh_circuit)
|
||
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]
|
||
|
||
from qiskit_ibm_runtime import Estimator
|
||
|
||
# Step 3: Execute using Qiskit Primitives.
|
||
num_ops = len(isa_observables)
|
||
|
||
batch_circuits = [chsh_isa_circuit] * number_of_phases * num_ops
|
||
batch_ops = [op for op in isa_observables for _ in individual_phases]
|
||
batch_phases = individual_phases * num_ops
|
||
|
||
estimator = Estimator(backend, options={"shots": int(1e4)})
|
||
job = estimator.run(batch_circuits, batch_ops, batch_phases)
|
||
expvals = job.result().values
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Use sessions and advanced options
|
||
|
||
Explore sessions and advanced options to optimize circuit performance on QPUs.
|
||
|
||
{/*Verified EstimatorV2. 2/23 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="EstimatorV2" label="Estimator V2">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit.quantum_info import SparsePauliOp, random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng(1234)
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
circuit = IQP(mat)
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
another_circuit = IQP(mat)
|
||
observable = SparsePauliOp("X" * n_qubits)
|
||
another_observable = SparsePauliOp("Y" * n_qubits)
|
||
|
||
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
|
||
isa_circuit = pm.run(circuit)
|
||
another_isa_circuit = pm.run(another_circuit)
|
||
isa_observable = observable.apply_layout(isa_circuit.layout)
|
||
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
with Session(backend=backend) as session:
|
||
estimator = Estimator()
|
||
|
||
estimator.options.resilience_level = 1
|
||
|
||
job = estimator.run([(isa_circuit, isa_observable)])
|
||
another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
|
||
result = job.result()
|
||
another_result = another_job.result()
|
||
|
||
# first job
|
||
print(f" > Expectation value: {result[0].data.evs}")
|
||
print(f" > Metadata: {result[0].metadata}")
|
||
|
||
# second job
|
||
print(f" > Another Expectation value: {another_result[0].data.evs}")
|
||
print(f" > More Metadata: {another_result[0].metadata}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="EstimatorV1" label="Estimator (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit.quantum_info import SparsePauliOp, random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Session, Estimator, Options
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng(1234)
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
circuit = IQP(mat)
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
another_circuit = IQP(mat)
|
||
observable = SparsePauliOp("X" * n_qubits)
|
||
another_observable = SparsePauliOp("Y" * n_qubits)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
another_isa_circuit = pm.run(another_circuit)
|
||
isa_observable = observable.apply_layout(isa_circuit.layout)
|
||
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)
|
||
|
||
options = Options()
|
||
options.optimization_level = 2
|
||
options.resilience_level = 2
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
with Session(backend=backend) as session:
|
||
estimator = Estimator(options=options)
|
||
job = estimator.run(isa_circuit, isa_observable)
|
||
another_job = estimator.run(another_isa_circuit, another_isa_observable)
|
||
result = job.result()
|
||
another_result = another_job.result()
|
||
|
||
# first job
|
||
print(f" > Expectation values job 1: {result.values}")
|
||
|
||
# second job
|
||
print(f" > Expectation values job 2: {another_result.values}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
## Steps to migrate to Sampler V2
|
||
|
||
1. Replace `from qiskit_ibm_runtime import Sampler` with `from qiskit_ibm_runtime import SamplerV2 as Sampler`.
|
||
2. Remove any `from qiskit_ibm_runtime import Options` statements, since the `Options` class is not used by V2 primitives. You can instead pass options as a dictionary when initializing the `SamplerV2` class (for example `sampler = Sampler(backend, options={“default_shots”: 1024})`), or set them after initialization:
|
||
```python
|
||
sampler = Sampler(backend)
|
||
sampler.options.default_shots = 1024
|
||
```
|
||
3. Review all the [supported options](/api/qiskit-ibm-runtime/options) and make updates accordingly.
|
||
4. Group each circuit you want to run with the observables and parameter values you want to apply to the circuit in a tuple (a PUB). For example, use `(circuit1, parameter_set1)` if you want to run `circuit1` with `parameter_set1`.
|
||
You can optionally specify the shots you want for that specific PUB.
|
||
5. Update the sampler `run()` method to pass in the list of PUBs. For example, `run([(circuit1, parameter_set1)])`.
|
||
You can optionally specify `shots` here, which would apply to all PUBs.
|
||
6. Sampler V2 job results are grouped by PUBs. You can see the output data for each PUB by indexing to it. While Sampler V2 returns unweighted samples, the result class has a convenience method to get counts instead. For example:
|
||
```python
|
||
pub_result = job.result()[0]
|
||
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
|
||
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
|
||
```
|
||
<Admonition type="note">
|
||
You need the classical register name to get the results. By default, it is named `meas` when you use `measure_all()`. When defining your circuit, if you create one or more classical registers with a non-default name, use that name to get the results. You can find the classical register name by running `<circuit_name>.cregs`. For example, `qc.cregs`.
|
||
</Admonition>
|
||
|
||
## Sampler full examples
|
||
|
||
### Run a single experiment
|
||
|
||
Use Sampler to determine the counts or quasi-probability distribution of a single circuit.
|
||
|
||
{/*Verified 2/28 */}
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
n_qubits = 127
|
||
|
||
mat = np.real(random_hermitian(n_qubits, seed=1234))
|
||
circuit = IQP(mat)
|
||
circuit.measure_all()
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run([isa_circuit])
|
||
result = job.result()
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
n_qubits = 127
|
||
|
||
mat = np.real(random_hermitian(n_qubits, seed=1234))
|
||
circuit = IQP(mat)
|
||
circuit.measure_all()
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run(circuit)
|
||
result = job.result()
|
||
|
||
print(f" > Quasi-probability distribution: {result.quasi_dists}")
|
||
print(f" > Metadata: {result.metadata}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Run multiple experiments in a single job
|
||
|
||
Use Sampler to determine the counts or quasi-probability distributions of multiple circuits in one job.
|
||
|
||
{/*Verified 2/28 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng()
|
||
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
|
||
circuits = [IQP(mat) for mat in mats]
|
||
for circuit in circuits:
|
||
circuit.measure_all()
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuits = pm.run(circuits)
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run(isa_circuits)
|
||
result = job.result()
|
||
|
||
for idx, pub_result in enumerate(result):
|
||
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng()
|
||
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
|
||
circuits = [IQP(mat) for mat in mats]
|
||
for circuit in circuits:
|
||
circuit.measure_all()
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run(circuits)
|
||
result = job.result()
|
||
|
||
print(f" > Quasi-probability distribution: {result.quasi_dists}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Run parameterized circuits
|
||
|
||
Run several experiments in a single job, leveraging parameter values to increase circuit reusability.
|
||
|
||
{/*Verified 2/28 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import RealAmplitudes
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||
|
||
# Step 1: Map classical inputs to a quantum problem
|
||
num_qubits = 127
|
||
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
|
||
circuit.measure_all()
|
||
|
||
# Define three sets of parameters for the circuit
|
||
rng = np.random.default_rng(1234)
|
||
parameter_values = [
|
||
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
|
||
]
|
||
|
||
# Step 2: Optimize problem for quantum execution.
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
|
||
# Step 3: Execute using Qiskit primitives.
|
||
|
||
from qiskit_ibm_runtime import SamplerV2 as Sampler
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run([(isa_circuit, parameter_values)])
|
||
result = job.result()
|
||
# Get results for the first (and only) PUB
|
||
pub_result = result[0]
|
||
# Get counts from the classical register "meas".
|
||
print(f" >> Counts for the meas output register: {pub_result.data.meas.get_counts()}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import RealAmplitudes
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||
|
||
# Step 1: Map classical inputs to a quantum problem
|
||
num_qubits = 5
|
||
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
|
||
circuit.measure_all()
|
||
|
||
# Define three sets of parameters for the circuit
|
||
rng = np.random.default_rng(1234)
|
||
parameter_values = [
|
||
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
|
||
]
|
||
|
||
# Step 2: Optimize problem for quantum execution.
|
||
|
||
service = QiskitRuntimeService()
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
|
||
# Step 3: Execute using Qiskit primitives.
|
||
|
||
from qiskit_ibm_runtime import Sampler
|
||
|
||
sampler = Sampler(backend)
|
||
job = sampler.run([isa_circuit] * 3, parameter_values)
|
||
result = job.result()
|
||
|
||
print(f" > Quasi-probability distribution: {result.quasi_dists}")
|
||
print(f" > Metadata: {result.metadata}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Use sessions and advanced options
|
||
|
||
Explore sessions and advanced options to optimize circuit performance on QPUs.
|
||
|
||
{/*Verified 2/28 */}
|
||
|
||
<Tabs>
|
||
<TabItem value="SamplerV2" label="Sampler V2">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler, Session
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng(1234)
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
circuit = IQP(mat)
|
||
circuit.measure_all()
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
another_circuit = IQP(mat)
|
||
another_circuit.measure_all()
|
||
|
||
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
|
||
isa_circuit = pm.run(circuit)
|
||
another_isa_circuit = pm.run(another_circuit)
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
# Turn on dynamical decoupling with sequence XpXm.
|
||
sampler.options.dynamical_decoupling.enable = True
|
||
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
with Session(backend=backend) as session:
|
||
sampler = Sampler()
|
||
job = sampler.run([isa_circuit])
|
||
another_job = sampler.run([another_isa_circuit])
|
||
result = job.result()
|
||
another_result = another_job.result()
|
||
|
||
# first job
|
||
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")
|
||
|
||
# second job
|
||
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")
|
||
```
|
||
</TabItem>
|
||
|
||
<TabItem value="SamplerV1" label="Sampler (V1)">
|
||
```python
|
||
import numpy as np
|
||
from qiskit.circuit.library import IQP
|
||
from qiskit.quantum_info import random_hermitian
|
||
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Session, Options
|
||
|
||
n_qubits = 127
|
||
|
||
rng = np.random.default_rng(1234)
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
circuit = IQP(mat)
|
||
circuit.measure_all()
|
||
mat = np.real(random_hermitian(n_qubits, seed=rng))
|
||
another_circuit = IQP(mat)
|
||
another_circuit.measure_all()
|
||
|
||
options = Options()
|
||
options.optimization_level = 2
|
||
options.resilience_level = 0
|
||
|
||
service = QiskitRuntimeService()
|
||
|
||
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
|
||
|
||
with Session(backend=backend) as session:
|
||
sampler = Sampler(options=options)
|
||
job = sampler.run(circuit)
|
||
another_job = sampler.run(another_circuit)
|
||
result = job.result()
|
||
another_result = another_job.result()
|
||
|
||
# first job
|
||
print(f" > Quasi-probability distribution job 1: {result.quasi_dists}")
|
||
|
||
# second job
|
||
print(f" > Quasi-probability distribution job 2: {another_result.quasi_dists}")
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|