-
Notifications
You must be signed in to change notification settings - Fork 127
/
Copy pathbackend_data.py
279 lines (248 loc) · 9.49 KB
/
backend_data.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# 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.
"""
Backend data access helper class
Since `BackendV1` and `BackendV2` do not share the same interface, this
class unifies data access for various data fields.
"""
from qiskit.providers.models import PulseBackendConfiguration
from qiskit.providers import BackendV1, BackendV2
from qiskit.providers.fake_provider import fake_backend, FakeBackendV2, FakeBackend
class BackendData:
"""Class for providing joint interface for accessing backend data"""
def __init__(self, backend):
"""Inits the backend and verifies version"""
self._backend = backend
self._v1 = isinstance(backend, BackendV1)
self._v2 = isinstance(backend, BackendV2)
if self._v2:
self._parse_additional_data()
def _parse_additional_data(self):
# data specific parsing not done yet in qiskit-terra
if hasattr(self._backend, "_conf_dict") and self._backend._conf_dict["open_pulse"]:
if "u_channel_lo" not in self._backend._conf_dict:
self._backend._conf_dict["u_channel_lo"] = [] # to avoid terra bug
self._pulse_conf = PulseBackendConfiguration.from_dict(self._backend._conf_dict)
@property
def name(self):
"""Returns the backend name"""
if self._v1:
return self._backend.name()
elif self._v2:
return self._backend.name
return str(self._backend)
def control_channel(self, qubits):
"""Returns the backend control channel for the given qubits"""
try:
if self._v1:
return self._backend.configuration().control(qubits)
elif self._v2:
try:
return self._backend.control_channel(qubits)
except NotImplementedError:
return self._pulse_conf.control(qubits)
except AttributeError:
return []
return []
def drive_channel(self, qubit):
"""Returns the backend drive channel for the given qubit"""
try:
if self._v1:
return self._backend.configuration().drive(qubit)
elif self._v2:
try:
return self._backend.drive_channel(qubit)
except NotImplementedError:
return self._pulse_conf.drive(qubit)
except AttributeError:
return None
return None
def measure_channel(self, qubit):
"""Returns the backend measure channel for the given qubit"""
try:
if self._v1:
return self._backend.configuration().measure(qubit)
elif self._v2:
try:
return self._backend.measure_channel(qubit)
except NotImplementedError:
return self._pulse_conf.measure(qubit)
except AttributeError:
return None
return None
def acquire_channel(self, qubit):
"""Returns the backend acquire channel for the given qubit"""
try:
if self._v1:
return self._backend.configuration().acquire(qubit)
elif self._v2:
try:
return self._backend.acquire_channel(qubit)
except NotImplementedError:
return self._pulse_conf.acquire(qubit)
except AttributeError:
return None
return None
@property
def granularity(self):
"""Returns the backend's time constraint granularity"""
try:
if self._v1:
return self._backend.configuration().timing_constraints.get("granularity", 1)
elif self._v2:
return self._backend.target.granularity
except AttributeError:
return 1
return 1
@property
def min_length(self):
"""Returns the backend's time constraint minimum duration"""
try:
if self._v1:
return self._backend.configuration().timing_constraints.get("min_length", 0)
elif self._v2:
return self._backend.target.min_length
except AttributeError:
return 0
return 0
@property
def pulse_alignment(self):
"""Returns the backend's time constraint pulse alignment"""
try:
if self._v1:
return self._backend.configuration().timing_constraints.get("pulse_alignment", 1)
elif self._v2:
return self._backend.target.pulse_alignment
except AttributeError:
return 1
return 1
@property
def acquire_alignment(self):
"""Returns the backend's time constraint acquire alignment"""
try:
if self._v1:
return self._backend.configuration().timing_constraints.get("acquire_alignment", 1)
elif self._v2:
# currently has a typo in terra
if hasattr(self._backend.target, "acquire_alignment"):
return self._backend.target.acquire_alignment
return self._backend.target.aquire_alignment
except AttributeError:
return 1
return 1
@property
def dt(self):
"""Returns the backend's input time resolution"""
if self._v1:
try:
return self._backend.configuration().dt
except AttributeError:
return None
elif self._v2:
return self._backend.dt
return None
@property
def max_circuits(self):
"""Returns the backend's max experiments value"""
if self._v1:
return getattr(self._backend.configuration(), "max_experiments", None)
elif self._v2:
return self._backend.max_circuits
return None
@property
def coupling_map(self):
"""Returns the backend's coupling map"""
if self._v1:
return getattr(self._backend.configuration(), "coupling_map", [])
elif self._v2:
coupling_map = self._backend.coupling_map
if coupling_map is None:
return coupling_map
return list(coupling_map.get_edges())
return []
@property
def version(self):
"""Returns the backend's version"""
if self._v1:
return getattr(self._backend, "version", None)
elif self._v2:
return self._backend.version
return None
@property
def provider(self):
"""Returns the backend's provider"""
try:
if self._v1:
return self._backend.provider()
elif self._v2:
return self._backend.provider
except AttributeError:
return None
return None
@property
def drive_freqs(self):
"""Returns the backend's qubit drive frequencies"""
if self._v1:
return getattr(self._backend.defaults(), "qubit_freq_est", [])
elif self._v2:
if self._backend.target.qubit_properties is None:
return []
return [property.frequency for property in self._backend.target.qubit_properties]
return []
@property
def meas_freqs(self):
"""Returns the backend's measurement stimulus frequencies.
.. note::
The qiskit-terra base classes do not provide this information as a
standard backend property, but it is available from some providers
in the data returned by the ``Backend.defaults()`` method.
"""
if not hasattr(self._backend, "defaults"):
return []
return getattr(self._backend.defaults(), "meas_freq_est", [])
@property
def num_qubits(self):
"""Returns the backend's number of qubits"""
if self._v1:
return self._backend.configuration().num_qubits
elif self._v2:
# meas_freq_est is currently not part of the BackendV2
return self._backend.num_qubits
return None
@property
def is_simulator(self):
"""Returns True given an indication the backend is a simulator
.. note::
For `BackendV2` we sometimes cannot be sure, because it lacks
a `simulator` field, as was present in `BackendV1`'s configuration.
We still check whether the backend inherits `FakeBackendV2`, for
either of its existing implementations in Terra.
"""
if self._v1:
if self._backend.configuration().simulator or isinstance(self._backend, FakeBackend):
return True
if self._v2:
if isinstance(self._backend, (FakeBackendV2, fake_backend.FakeBackendV2)):
return True
return False
def qubit_t1(self, qubit: int) -> float:
"""Return the T1 value for a qubit from the backend properties
Args:
qubit: the qubit index to return T1 for
Returns:
The T1 value
"""
if self._v1:
return self._backend.properties().qubit_property(qubit)["T1"][0]
if self._v2:
return self._backend.qubit_properties(qubit).t1
return float("nan")