Help

Get started with primitives for Qiskit Runtime

Quantum primitives are foundational building blocks for designing and optimizing quantum workloads. They provide options to customize the iteration and execution of programs to maximize the solution quality from IBM quantum processing units (QPUs).

How they work

Estimator

Calculate expectation values

Efficiently calculate and interpret expectation values of the quantum operators required for many algorithms with Estimator. Explore uses in molecular modeling, machine learning, and complex optimization problems.

View API Reference
graph

Try out Estimator

To run these code samples please follow the quick start instructions found here.

Run a single experiment

Use Estimator to determine the expectation value of a single circuit-observable pair

1import numpy as np
2from qiskit.circuit.library import IQP
3from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
4from qiskit.quantum_info import SparsePauliOp, random_hermitian
5from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
6
7n_qubits = 127
8
9service = QiskitRuntimeService()
10backend = service.least_busy(operational=True, simulator=False, min_num_qubits=n_qubits)
11
12mat = np.real(random_hermitian(n_qubits, seed=1234))
13circuit = IQP(mat)
14observable = SparsePauliOp("Z" * n_qubits)
15
16pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
17isa_circuit = pm.run(circuit)
18isa_observable = observable.apply_layout(isa_circuit.layout)
19
20estimator = Estimator(backend)
21job = estimator.run([(isa_circuit, isa_observable)])
22result = job.result()
23
24print(f" > Expectation value: {result[0].data.evs}")
25print(f" > Metadata: {result[0].metadata}")
26

Run multiple experiments in a single job

Use Estimator to determine the expectation values of multiple circuit-observable pairs

1import numpy as np
2from qiskit.circuit.library import IQP
3from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
4from qiskit.quantum_info import SparsePauliOp, random_hermitian
5from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
6
7n_qubits = 127
8
9service = QiskitRuntimeService()
10backend = service.least_busy(operational=True, simulator=False, min_num_qubits=n_qubits)
11
12rng = np.random.default_rng()
13mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
14
15pubs = []
16circuits = [IQP(mat) for mat in mats]
17observables = [
18 SparsePauliOp("X" * n_qubits),
19 SparsePauliOp("Y" * n_qubits),
20 SparsePauliOp("Z" * n_qubits),
21]
22
23# Get ISA circuits
24pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
25
26for qc, obs in zip(circuits, observables):
27 isa_circuit = pm.run(qc)
28 isa_obs = obs.apply_layout(isa_circuit.layout)
29 pubs.append((isa_circuit, isa_obs))
30
31estimator = Estimator(backend)
32job = estimator.run(pubs)
33job_result = job.result()
34
35for idx in range(len(pubs)):
36 pub_result = job_result[idx]
37 print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
38 print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")
39

Run parametrized circuits

Run 3 experiments in a single job, leveraging parameter values to increase circuit reusability

1import numpy as np
2
3from qiskit.circuit import QuantumCircuit, Parameter
4from qiskit.quantum_info import SparsePauliOp
5from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
6from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
7
8service = QiskitRuntimeService()
9backend = service.least_busy(operational=True, simulator=False)
10
11# Step 1: Map classical inputs to a quantum problem
12theta = Parameter("θ")
13
14chsh_circuit = QuantumCircuit(2)
15chsh_circuit.h(0)
16chsh_circuit.cx(0, 1)
17chsh_circuit.ry(theta, 0)
18
19number_of_phases = 21
20phases = np.linspace(0, 2 * np.pi, number_of_phases)
21individual_phases = [[ph] for ph in phases]
22
23ZZ = SparsePauliOp.from_list([("ZZ", 1)])
24ZX = SparsePauliOp.from_list([("ZX", 1)])
25XZ = SparsePauliOp.from_list([("XZ", 1)])
26XX = SparsePauliOp.from_list([("XX", 1)])
27ops = [ZZ, ZX, XZ, XX]
28
29# Step 2: Optimize problem for quantum execution.
30
31pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
32chsh_isa_circuit = pm.run(chsh_circuit)
33isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]
34
35# Step 3: Execute using Qiskit primitives.
36
37# Reshape observable array for broadcasting
38reshaped_ops = np.fromiter(isa_observables, dtype=object)
39reshaped_ops = reshaped_ops.reshape((4, 1))
40
41estimator = Estimator(backend, options={"default_shots": int(1e4)})
42job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
43# Get results for the first (and only) PUB
44pub_result = job.result()[0]
45print(f">>> Expectation values: {pub_result.data.evs}")
46print(f">>> Standard errors: {pub_result.data.stds}")
47print(f">>> Metadta: {pub_result.metadata}")
48

Leverage sessions and advanced options

Explore session and advanced options to optimize circuit performance on QPUs

1import numpy as np
2from qiskit.circuit.library import IQP
3from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
4from qiskit.quantum_info import SparsePauliOp, random_hermitian
5from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator
6
7n_qubits = 127
8
9service = QiskitRuntimeService()
10backend = service.least_busy(operational=True, simulator=False, min_num_qubits=n_qubits)
11
12rng = np.random.default_rng(1234)
13mat = np.real(random_hermitian(n_qubits, seed=rng))
14circuit = IQP(mat)
15mat = np.real(random_hermitian(n_qubits, seed=rng))
16another_circuit = IQP(mat)
17observable = SparsePauliOp("X" * n_qubits)
18another_observable = SparsePauliOp("Y" * n_qubits)
19
20pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
21isa_circuit = pm.run(circuit)
22another_isa_circuit = pm.run(another_circuit)
23isa_observable = observable.apply_layout(isa_circuit.layout)
24another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)
25
26with Session(service=service, backend=backend) as session:
27 estimator = Estimator(mode=session)
28
29 estimator.options.optimization_level = 1
30 estimator.options.resilience_level = 1
31
32 job = estimator.run([(isa_circuit, isa_observable)])
33 another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
34 result = job.result()
35 another_result = another_job.result()
36
37 # first job
38 print(f" > Expectation value: {result[0].data.evs}")
39 print(f" > Metadata: {result[0].metadata}")
40
41 # second job
42 print(f" > Another Expectation value: {another_result[0].data.evs}")
43 print(f" > More Metadata: {another_result[0].metadata}")
44