0
# Quantum Primitives
1
2
Qiskit's primitives provide the modern interface for quantum computation using the V2 interface. They offer two core computational primitives: StatevectorSampler for circuit execution and measurement, and StatevectorEstimator for expectation value calculation. This interface uses Primitive Unified Blocs (PUBs) for efficient vectorized computation.
3
4
## Capabilities
5
6
### Sampler V2 Primitive
7
8
The StatevectorSampler primitive executes quantum circuits and returns measurement samples, providing the foundation for sampling-based quantum algorithms.
9
10
```python { .api }
11
class StatevectorSampler(BaseSamplerV2):
12
def __init__(self, *, default_shots=1024, seed=None):
13
"""
14
Initialize StatevectorSampler primitive (V2 interface).
15
16
Parameters:
17
- default_shots: Default number of measurement shots
18
- seed: Random seed for reproducible results
19
"""
20
21
def run(self, pubs, *, shots=None):
22
"""
23
Execute primitive unified blocs (PUBs) and return measurement results.
24
25
Parameters:
26
- pubs: Primitive unified blocs - list of (circuit, parameter_values, shots) tuples
27
- shots: Default shots for all PUBs (overridden by per-PUB shots)
28
29
Returns:
30
PrimitiveJob: Job object with result() method
31
"""
32
33
class BackendSamplerV2(BaseSamplerV2):
34
def __init__(self, backend, *, default_shots=1024, seed=None):
35
"""
36
Backend-based Sampler V2 implementation.
37
38
Parameters:
39
- backend: Quantum backend to execute circuits on
40
- default_shots: Default number of shots
41
- seed: Random seed
42
"""
43
44
def run(self, pubs, *, shots=None):
45
"""Execute PUBs on backend."""
46
47
@property
48
def backend(self):
49
"""Backend used for execution."""
50
51
@property
52
def options(self):
53
"""Execution options."""
54
55
class SamplerPubResult(PubResult):
56
"""Result for a single PUB execution in sampler."""
57
58
@property
59
def data(self):
60
"""
61
Measurement data as DataBin with register-named BitArrays.
62
Access measurement outcomes via register names (e.g., result.data.c for register 'c').
63
"""
64
65
@property
66
def metadata(self):
67
"""PUB execution metadata."""
68
```
69
70
### Estimator V2 Primitive
71
72
The StatevectorEstimator primitive calculates expectation values of observables on quantum states, essential for variational quantum algorithms and quantum chemistry.
73
74
```python { .api }
75
class StatevectorEstimator(BaseEstimatorV2):
76
def __init__(self, *, default_precision=0.015625, seed=None):
77
"""
78
Initialize StatevectorEstimator primitive (V2 interface).
79
80
Parameters:
81
- default_precision: Default precision for expectation values
82
- seed: Random seed for reproducible results
83
"""
84
85
def run(self, pubs, *, precision=None):
86
"""
87
Calculate expectation values for primitive unified blocs.
88
89
Parameters:
90
- pubs: List of (circuit, observables, parameter_values) tuples
91
- precision: Target precision for expectation values
92
93
Returns:
94
PrimitiveJob: Job object with result() method
95
"""
96
97
class BackendEstimatorV2(BaseEstimatorV2):
98
def __init__(self, backend, *, default_precision=0.015625, seed=None):
99
"""
100
Backend-based Estimator V2 implementation.
101
102
Parameters:
103
- backend: Quantum backend to execute circuits on
104
- default_precision: Default precision
105
- seed: Random seed
106
"""
107
108
def run(self, pubs, *, precision=None):
109
"""Calculate expectation values on backend."""
110
111
@property
112
def backend(self):
113
"""Backend used for execution."""
114
115
@property
116
def options(self):
117
"""Execution options."""
118
119
class EstimatorPubResult(PubResult):
120
"""Result for a single PUB execution in estimator."""
121
122
@property
123
def data(self):
124
"""
125
Expectation value data as DataBin with evs array.
126
Access expectation values via result.data.evs.
127
"""
128
129
@property
130
def metadata(self):
131
"""PUB calculation metadata."""
132
```
133
134
### Base Classes
135
136
Abstract base classes defining the V2 primitive interfaces.
137
138
```python { .api }
139
class BaseSamplerV2:
140
"""Abstract base class for V2 Sampler implementations."""
141
142
def run(self, pubs, *, shots=None):
143
"""Run primitive unified blocs and return measurement results."""
144
raise NotImplementedError
145
146
class BaseEstimatorV2:
147
"""Abstract base class for V2 Estimator implementations."""
148
149
def run(self, pubs, *, precision=None):
150
"""Calculate expectation values for primitive unified blocs."""
151
raise NotImplementedError
152
```
153
154
### Primitive Jobs and Results
155
156
Job management and result handling for primitive execution.
157
158
```python { .api }
159
class PrimitiveJob(BasePrimitiveJob):
160
"""Job object returned by primitive run() methods."""
161
162
def result(self):
163
"""
164
Get job result (blocking).
165
166
Returns:
167
PrimitiveResult: Execution results containing list of PubResults
168
"""
169
170
def status(self):
171
"""Get job status."""
172
173
def cancel(self):
174
"""Cancel the job if possible."""
175
176
def running(self):
177
"""Check if job is currently running."""
178
179
def done(self):
180
"""Check if job is completed."""
181
182
class PrimitiveResult:
183
"""Result from V2 primitive execution containing multiple PubResults."""
184
185
def __getitem__(self, index):
186
"""Access individual PubResult by index."""
187
188
def __len__(self):
189
"""Number of PubResults."""
190
191
def __iter__(self):
192
"""Iterate over PubResults."""
193
194
class PubResult:
195
"""Base class for primitive unified bloc results."""
196
197
@property
198
def data(self):
199
"""Result data as DataBin."""
200
201
@property
202
def metadata(self):
203
"""Result metadata."""
204
```
205
206
### Container Classes
207
208
Data structures for handling primitive inputs and outputs with V2 interface.
209
210
```python { .api }
211
class BitArray:
212
"""Efficient storage of measurement bit arrays organized by classical register."""
213
214
def __init__(self, array, num_bits, shape=None):
215
"""
216
Initialize bit array.
217
218
Parameters:
219
- array: Array of measurement outcomes
220
- num_bits: Number of bits per measurement
221
- shape: Shape of measurement array
222
"""
223
224
def get_counts(self, loc=None):
225
"""
226
Get measurement counts dictionary.
227
228
Parameters:
229
- loc: Specific bit locations to count
230
231
Returns:
232
dict: Counts dictionary mapping bitstrings to frequencies
233
"""
234
235
def get_bitstrings(self, loc=None):
236
"""Get measurement results as bit strings."""
237
238
def slice_bits(self, indices):
239
"""Extract specific bit positions."""
240
241
@property
242
def shape(self):
243
"""Shape of the bit array."""
244
245
@property
246
def num_bits(self):
247
"""Number of bits per measurement."""
248
249
@property
250
def num_shots(self):
251
"""Number of measurement shots."""
252
253
class DataBin:
254
"""Container for primitive execution data with register-based access."""
255
256
def __init__(self, **data):
257
"""Initialize data container with named data fields."""
258
259
def __getattr__(self, name):
260
"""Access data fields as attributes (e.g., data.c for register 'c')."""
261
262
def __getitem__(self, key):
263
"""Access data fields by key."""
264
265
def keys(self):
266
"""Get data field names."""
267
268
def values(self):
269
"""Get data field values."""
270
271
def items(self):
272
"""Get (name, value) pairs."""
273
```
274
275
### Primitive Unified Blocs (PUBs)
276
277
Input specification format for V2 primitives enabling vectorized computation.
278
279
```python { .api }
280
# Type aliases for PUB specifications
281
SamplerPubLike = Union[
282
QuantumCircuit, # Simple circuit
283
Tuple[QuantumCircuit, ArrayLike], # Circuit with parameter values
284
Tuple[QuantumCircuit, ArrayLike, int], # Circuit with parameters and shots
285
]
286
287
EstimatorPubLike = Union[
288
Tuple[QuantumCircuit, ObservablesArrayLike], # Circuit and observables
289
Tuple[QuantumCircuit, ObservablesArrayLike, ArrayLike], # With parameter values
290
]
291
292
ObservablesArrayLike = Union[
293
str, # Pauli string
294
SparsePauliOp, # Sparse Pauli operator
295
List[Union[str, SparsePauliOp]], # List of observables
296
]
297
298
BindingsArrayLike = Union[
299
Mapping[Parameter, float], # Single parameter binding
300
Sequence[Mapping[Parameter, float]], # Multiple parameter sets
301
ArrayLike, # Array of parameter values
302
]
303
```
304
305
### Usage Examples
306
307
```python
308
from qiskit.primitives import StatevectorSampler, StatevectorEstimator
309
from qiskit import QuantumCircuit, Parameter
310
from qiskit.quantum_info import SparsePauliOp
311
import numpy as np
312
313
# Basic V2 Sampler usage
314
circuit = QuantumCircuit(2, 2, name='bell')
315
circuit.h(0)
316
circuit.cx(0, 1)
317
circuit.measure_all()
318
319
sampler = StatevectorSampler()
320
job = sampler.run([circuit], shots=1000)
321
result = job.result()
322
pub_result = result[0] # First (and only) PUB result
323
324
# Access measurement data by register name
325
counts = pub_result.data.c.get_counts() # 'c' is the classical register name
326
print(f"Measurement counts: {counts}")
327
328
# Parameterized circuit with V2 interface (PUB format)
329
theta = Parameter('θ')
330
param_circuit = QuantumCircuit(1, 1)
331
param_circuit.ry(theta, 0)
332
param_circuit.measure_all()
333
334
# Run with multiple parameter values using PUB format
335
angles = [[0], [np.pi/4], [np.pi/2], [np.pi]]
336
job = sampler.run([(param_circuit, angles)], shots=1024)
337
result = job.result()
338
339
# Access results for different parameter values
340
param_result = result[0]
341
measurement_data = param_result.data.c # BitArray with shape matching parameter array
342
for i, angle in enumerate([0, np.pi/4, np.pi/2, np.pi]):
343
counts = measurement_data.get_counts(i) # Get counts for i-th parameter set
344
print(f"θ = {angle:.2f}: {counts}")
345
346
# V2 Estimator usage with PUB format
347
observable = SparsePauliOp.from_list([('ZZ', 1.0), ('XX', 0.5)])
348
prep_circuit = QuantumCircuit(2)
349
prep_circuit.h(0)
350
prep_circuit.cx(0, 1) # Bell state preparation
351
352
estimator = StatevectorEstimator()
353
job = estimator.run([(prep_circuit, observable)])
354
result = job.result()
355
expectation_value = result[0].data.evs[0] # Access expectation values via .evs
356
print(f"Expectation value: {expectation_value}")
357
358
# Vectorized estimator computation
359
multiple_observables = [
360
SparsePauliOp.from_list([('ZZ', 1.0)]),
361
SparsePauliOp.from_list([('XX', 1.0)]),
362
SparsePauliOp.from_list([('YY', 1.0)])
363
]
364
365
# Single PUB with multiple observables (vectorized)
366
job = estimator.run([(prep_circuit, multiple_observables)], precision=0.01)
367
result = job.result()
368
expectation_values = result[0].data.evs # Array of expectation values
369
print(f"Multiple expectations: {expectation_values}")
370
371
# Variational algorithm example with V2 primitives
372
def ansatz_circuit(params):
373
circuit = QuantumCircuit(2)
374
circuit.ry(params[0], 0)
375
circuit.ry(params[1], 1)
376
circuit.cx(0, 1)
377
circuit.ry(params[2], 0)
378
circuit.ry(params[3], 1)
379
return circuit
380
381
# Hamiltonian for optimization
382
hamiltonian = SparsePauliOp.from_list([
383
('ZZ', 1.0),
384
('XX', 0.5),
385
('YY', 0.5),
386
('ZI', -0.1),
387
('IZ', -0.1)
388
])
389
390
# Multiple parameter sets in a single PUB for efficiency
391
param_sets = [
392
[0, 0, 0, 0],
393
[np.pi/4, np.pi/4, np.pi/4, np.pi/4],
394
[np.pi/2, 0, np.pi/2, 0]
395
]
396
397
ansatz = ansatz_circuit([Parameter(f'θ{i}') for i in range(4)])
398
job = estimator.run([(ansatz, hamiltonian, param_sets)])
399
result = job.result()
400
401
# Access energy values for all parameter sets at once
402
energies = result[0].data.evs
403
for i, params in enumerate(param_sets):
404
print(f"Parameters {params}: Energy = {energies[i]:.4f}")
405
406
# Backend execution with V2 primitives
407
from qiskit.primitives import BackendSamplerV2, BackendEstimatorV2
408
from qiskit.providers.fake_provider import FakeManila
409
410
backend = FakeManila()
411
backend_sampler = BackendSamplerV2(backend)
412
413
# V2 backend execution
414
job = backend_sampler.run([circuit], shots=2048)
415
result = job.result()
416
backend_counts = result[0].data.c.get_counts()
417
print(f"Backend execution result: {backend_counts}")
418
```