Get started with the Estimator primitive
While this documentation uses the primitives from Qiskit Runtime, which allow you to use IBM® backends, the primitives can be run on any provider by using the backend primitives instead. Additionally, you can use the reference primitives to run on a local statevector simulator. See Exact simulation with Qiskit primitives for details.
The steps in this topic describe how to set up primitives, explore the options you can use to configure them, and invoke them in a program.
To use the newly supported fractional gates, set use_fractional_gates=True when requesting a backend from a QiskitRuntimeService instance. For example:
service = QiskitRuntimeService()
fractional_gate_backend = service.least_busy(use_fractional_gates=True)Note that this is an experimental feature and might change in the future.
Package versions
The code on this page was developed using the following requirements. We recommend using these versions or newer.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Steps to use the Estimator primitive
1. Initialize the account
Because Qiskit Runtime Estimator is a managed service, you first need to initialize your account. You can then select the QPU you want to use to calculate the expectation value.
Follow the steps in the Install and set up topic if you don't already have an account.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
print(backend.name)Output:
ibm_torino
2. Create a circuit and an observable
You need at least one circuit and one observable as inputs to the Estimator primitive.
from qiskit.circuit.library import qaoa_ansatz
from qiskit.quantum_info import SparsePauliOp
entanglement = [tuple(edge) for edge in backend.coupling_map.get_edges()]
observable = SparsePauliOp.from_sparse_list(
[("ZZ", [i, j], 0.5) for i, j in entanglement],
num_qubits=backend.num_qubits,
)
circuit = qaoa_ansatz(observable, reps=2)
# the circuit is parametrized, so we will define the parameter values for execution
param_values = [0.1, 0.2, 0.3, 0.4]
print(f">>> Observable: {observable.paulis}")Output:
>>> Observable: ['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...', ...]
The circuit and observable need to be transformed to only use instructions supported by the QPU (referred to as instruction set architecture (ISA) circuits). We'll use the transpiler to do this.
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print(f">>> Circuit ops (ISA): {isa_circuit.count_ops()}")Output:
>>> Circuit ops (ISA): OrderedDict([('rz', 3826), ('sx', 1601), ('cz', 968)])
3. Initialize Qiskit Runtime Estimator
When you initialize the Estimator, use the mode parameter to specify the mode you want it to run in. Possible values are batch, session, or backend objects for batch, session, and job execution mode, respectively. For more information, see Introduction to Qiskit Runtime execution modes.
from qiskit_ibm_runtime import EstimatorV2 as Estimator
estimator = Estimator(mode=backend)4. Invoke the Estimator and get results
Next, invoke the run() method to calculate expectation values for the input circuits and observables. The circuit, observable, and optional parameter value sets are input as primitive unified bloc (PUB) tuples.
job = estimator.run([(isa_circuit, isa_observable, param_values)])
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")Output:
>>> Job ID: d5k96c4jt3vs73ds5smg
>>> Job Status: QUEUED
result = job.result()
print(f">>> {result}")
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")Output:
>>> PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})
> Expectation value: 25.8930784649363
> Metadata: {'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32}
Get started with the Estimator backend primitive
Unlike provider-specific primitives, backend primitives are generic implementations that can be used with an arbitrary
backend object, as long as it implements the Backend interface.
The Estimator primitive can be run with any provider by using qiskit.primitives.BackendEstimatorV2.
Some providers implement primitives natively. See the Qiskit Ecosystem page for details.
Example:
from qiskit.primitives import BackendEstimatorV2
from <some_qiskit_provider> import QiskitProvider
provider = QiskitProvider()
backend = provider.get_backend('backend_name')
estimator = BackendEstimatorV2(backend)Similarities and differences between backend and Runtime primitives
-
The inputs to and outputs from
qiskit.primitives.BackendEstimatorV2follow the same PUB format as the primitives in Qiskit Runtime. See PUBs for details. However, there can be differences in the fields of the returned metadata. -
The
qiskit.primitives.BackendEstimatorV2class offers no measurement or gate error mitigation implementations out-of-the-box, as backend primitives are designed to run locally in the user's machine. -
The backend primitive interfaces expose custom
EstimatorV2Optionsthat are different from the Runtime implementations.
Next steps
- Learn how to test locally before running on quantum computers.
- Review detailed primitives examples.
- Practice with primitives by working through the Cost function lesson in IBM Quantum Learning.
- Learn how to transpile locally in the Transpile section.
- Try the Compare transpiler settings guide.
- Learn how to use the primitive options.
- View the API for Estimator options.
- Read Migrate to V2 primitives.