/
base_sampler.py
180 lines (139 loc) · 5.89 KB
/
base_sampler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Base Sampler Classes"""
from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Iterable, Sequence
from copy import copy
from typing import Generic, TypeVar
from qiskit.circuit import QuantumCircuit
from qiskit.providers import JobV1 as Job
from ..containers.primitive_result import PrimitiveResult
from ..containers.pub_result import PubResult
from ..containers.sampler_pub import SamplerPubLike
from . import validation
from .base_primitive import BasePrimitive
from .base_primitive_job import BasePrimitiveJob
T = TypeVar("T", bound=Job)
class BaseSamplerV1(BasePrimitive, Generic[T]):
r"""Sampler V1 base class
Base class of Sampler that calculates quasi-probabilities of bitstrings from quantum circuits.
A sampler is initialized with an empty parameter set. The sampler is used to
create a :class:`~qiskit.providers.JobV1`, via the :meth:`qiskit.primitives.Sampler.run()`
method. This method is called with the following parameters
* quantum circuits (:math:`\psi_i(\theta)`): list of (parameterized) quantum circuits.
(a list of :class:`~qiskit.circuit.QuantumCircuit` objects)
* parameter values (:math:`\theta_k`): list of sets of parameter values
to be bound to the parameters of the quantum circuits.
(list of list of float)
The method returns a :class:`~qiskit.providers.JobV1` object, calling
:meth:`qiskit.providers.JobV1.result()` yields a :class:`~qiskit.primitives.SamplerResult`
object, which contains probabilities or quasi-probabilities of bitstrings,
plus optional metadata like error bars in the samples.
Here is an example of how sampler is used.
.. code-block:: python
from qiskit.primitives import Sampler
from qiskit import QuantumCircuit
from qiskit.circuit.library import RealAmplitudes
# a Bell circuit
bell = QuantumCircuit(2)
bell.h(0)
bell.cx(0, 1)
bell.measure_all()
# two parameterized circuits
pqc = RealAmplitudes(num_qubits=2, reps=2)
pqc.measure_all()
pqc2 = RealAmplitudes(num_qubits=2, reps=3)
pqc2.measure_all()
theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 2, 3, 4, 5, 6, 7]
# initialization of the sampler
sampler = Sampler()
# Sampler runs a job on the Bell circuit
job = sampler.run(circuits=[bell], parameter_values=[[]], parameters=[[]])
job_result = job.result()
print([q.binary_probabilities() for q in job_result.quasi_dists])
# Sampler runs a job on the parameterized circuits
job2 = sampler.run(
circuits=[pqc, pqc2],
parameter_values=[theta1, theta2],
parameters=[pqc.parameters, pqc2.parameters])
job_result = job2.result()
print([q.binary_probabilities() for q in job_result.quasi_dists])
"""
__hash__ = None
def __init__(
self,
*,
options: dict | None = None,
):
"""
Args:
options: Default options.
"""
super().__init__(options)
def run(
self,
circuits: QuantumCircuit | Sequence[QuantumCircuit],
parameter_values: Sequence[float] | Sequence[Sequence[float]] | None = None,
**run_options,
) -> T:
"""Run the job of the sampling of bitstrings.
Args:
circuits: One of more circuit objects.
parameter_values: Parameters to be bound to the circuit.
run_options: Backend runtime options used for circuit execution.
Returns:
The job object of the result of the sampler. The i-th result corresponds to
``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``.
Raises:
ValueError: Invalid arguments are given.
"""
# Validation
circuits, parameter_values = validation._validate_sampler_args(circuits, parameter_values)
# Options
run_opts = copy(self.options)
run_opts.update_options(**run_options)
return self._run(
circuits,
parameter_values,
**run_opts.__dict__,
)
@abstractmethod
def _run(
self,
circuits: tuple[QuantumCircuit, ...],
parameter_values: tuple[tuple[float, ...], ...],
**run_options,
) -> T:
raise NotImplementedError("The subclass of BaseSampler must implement `_run` method.")
BaseSampler = BaseSamplerV1
class BaseSamplerV2(ABC):
r"""Sampler V2 base class.
A Sampler returns samples of quantum circuit outputs.
All sampler implementations must implement default value for the ``shots`` in the
:meth:`.run` method if ``None`` is given both as a ``kwarg`` and in all of the pubs.
"""
@abstractmethod
def run(
self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None
) -> BasePrimitiveJob[PrimitiveResult[PubResult]]:
"""Run and collect samples from each pub.
Args:
pubs: An iterable of pub-like objects. For example, a list of circuits
or tuples ``(circuit, parameter_values)``.
shots: The total number of shots to sample for each sampler pub that does
not specify its own shots. If ``None``, the primitive's default
shots value will be used, which can vary by implementation.
Returns:
The job object of Sampler's result.
"""