iqm-benchmarks 2.32__py3-none-any.whl → 2.33__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iqm-benchmarks might be problematic. Click here for more details.
- iqm/benchmarks/__init__.py +4 -0
- iqm/benchmarks/compressive_gst/gst_analysis.py +0 -1
- iqm/benchmarks/entanglement/graph_states.py +1 -1
- iqm/benchmarks/randomized_benchmarking/direct_rb/__init__.py +19 -0
- iqm/benchmarks/randomized_benchmarking/direct_rb/direct_rb.py +1000 -0
- iqm/benchmarks/randomized_benchmarking/eplg/__init__.py +19 -0
- iqm/benchmarks/randomized_benchmarking/eplg/eplg.py +409 -0
- iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py +1 -1
- iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py +81 -163
- iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py +310 -71
- iqm/benchmarks/utils.py +138 -53
- iqm/benchmarks/utils_plots.py +189 -3
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.33.dist-info}/METADATA +3 -1
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.33.dist-info}/RECORD +17 -13
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.33.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.33.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.33.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright 2024 IQM Benchmarks developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Error Per Layered Gate (EPLG) estimates the layer fidelity of 2Q gate layers.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from . import eplg
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error Per Layered Gate (EPLG).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from time import strftime
|
|
6
|
+
from typing import Dict, Optional, Sequence, Tuple, Type, cast
|
|
7
|
+
|
|
8
|
+
from matplotlib.figure import Figure
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import networkx as nx
|
|
11
|
+
import numpy as np
|
|
12
|
+
from qiskit.transpiler import CouplingMap
|
|
13
|
+
from uncertainties import ufloat
|
|
14
|
+
import xarray as xr
|
|
15
|
+
|
|
16
|
+
from iqm.benchmarks import (
|
|
17
|
+
Benchmark,
|
|
18
|
+
BenchmarkAnalysisResult,
|
|
19
|
+
BenchmarkCircuit,
|
|
20
|
+
BenchmarkObservation,
|
|
21
|
+
BenchmarkObservationIdentifier,
|
|
22
|
+
BenchmarkRunResult,
|
|
23
|
+
)
|
|
24
|
+
from iqm.benchmarks.benchmark import BenchmarkConfigurationBase
|
|
25
|
+
from iqm.benchmarks.logging_config import qcvv_logger
|
|
26
|
+
from iqm.benchmarks.randomized_benchmarking.direct_rb.direct_rb import (
|
|
27
|
+
DirectRandomizedBenchmarking,
|
|
28
|
+
DirectRBConfiguration,
|
|
29
|
+
direct_rb_analysis,
|
|
30
|
+
)
|
|
31
|
+
from iqm.benchmarks.utils import split_into_disjoint_pairs
|
|
32
|
+
from iqm.benchmarks.utils_plots import GraphPositions, draw_graph_edges, evaluate_hamiltonian_paths, rx_to_nx_graph
|
|
33
|
+
from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def plot_layered_fidelities_graph(
|
|
37
|
+
fidelities: Dict[str, Dict[str, float]],
|
|
38
|
+
backend_coupling_map: CouplingMap,
|
|
39
|
+
qubit_names: Dict[int, str],
|
|
40
|
+
timestamp: str,
|
|
41
|
+
station: Optional[str] = None,
|
|
42
|
+
eplg_estimate: Optional[Dict[str, float]] = None,
|
|
43
|
+
) -> Tuple[str, Figure]:
|
|
44
|
+
"""Plots the layered fidelity for each corresponding pair of qubits in a graph layout of the given backend.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
fidelities (Dict[str, Dict[str, float]]): A dictionary (str qubit keys) of dictionaries (keys "value"/"uncertainty") of fidelities (float) to plot.
|
|
48
|
+
backend_coupling_map (CouplingMap): The CouplingMap instance.
|
|
49
|
+
qubit_names (Dict[int, str]): A dictionary of qubit names corresponding to qubit indices.
|
|
50
|
+
timestamp (str): The timestamp of the corresponding experiment.
|
|
51
|
+
station (str): The name of the station to use for the graph layout.
|
|
52
|
+
eplg_estimate (Optional[Dict[str, float]]): A dictionary with the EPLG estimate value and its uncertainty.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Tuple[str, Figure]: The figure label and the layered fidelities plot figure.
|
|
56
|
+
"""
|
|
57
|
+
num_qubits = len(qubit_names.keys())
|
|
58
|
+
fig_name = (
|
|
59
|
+
f"layered_fidelities_graph_{station}_{timestamp}"
|
|
60
|
+
if station is not None
|
|
61
|
+
else f"layered_fidelities_graph_{timestamp}"
|
|
62
|
+
)
|
|
63
|
+
# Sort the fidelities by value
|
|
64
|
+
sorted_fidelities = dict(sorted(fidelities.items(), key=lambda item: item[1]["value"]))
|
|
65
|
+
|
|
66
|
+
qubit_pairs = [
|
|
67
|
+
tuple(int(num) for num in x.replace("(", "").replace(")", "").replace("...", "").split(", "))
|
|
68
|
+
for x in sorted_fidelities.keys()
|
|
69
|
+
]
|
|
70
|
+
fidelity_values = [100 * a["value"] for a in sorted_fidelities.values()]
|
|
71
|
+
|
|
72
|
+
fidelity_edges = dict(zip(qubit_pairs, fidelity_values))
|
|
73
|
+
|
|
74
|
+
cmap = plt.cm.get_cmap("winter")
|
|
75
|
+
|
|
76
|
+
fig = plt.figure()
|
|
77
|
+
ax = plt.axes()
|
|
78
|
+
|
|
79
|
+
if station is not None:
|
|
80
|
+
if station.lower() in GraphPositions.predefined_stations:
|
|
81
|
+
qubit_positions = GraphPositions.predefined_stations[station.lower()]
|
|
82
|
+
else:
|
|
83
|
+
if num_qubits in (20, 7):
|
|
84
|
+
station = "garnet" if num_qubits == 20 else "deneb"
|
|
85
|
+
qubit_positions = GraphPositions.predefined_stations[station]
|
|
86
|
+
else:
|
|
87
|
+
graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
|
|
88
|
+
qubit_positions = GraphPositions.create_positions(graph_backend)
|
|
89
|
+
else:
|
|
90
|
+
graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
|
|
91
|
+
if num_qubits in (20, 7):
|
|
92
|
+
station = "garnet" if num_qubits == 20 else "deneb"
|
|
93
|
+
qubit_positions = GraphPositions.predefined_stations[station]
|
|
94
|
+
else:
|
|
95
|
+
qubit_positions = GraphPositions.create_positions(graph_backend)
|
|
96
|
+
|
|
97
|
+
# Normalize fidelity values to the range [0, 1] for color mapping
|
|
98
|
+
norm = plt.Normalize(vmin=cast(float, min(fidelity_values)), vmax=cast(float, max(fidelity_values)))
|
|
99
|
+
edge_colors = [cmap(norm(fidelity_edges[edge])) for edge in qubit_pairs]
|
|
100
|
+
|
|
101
|
+
nx.draw_networkx(
|
|
102
|
+
rx_to_nx_graph(backend_coupling_map),
|
|
103
|
+
pos=qubit_positions,
|
|
104
|
+
nodelist=list(range(num_qubits)),
|
|
105
|
+
labels={x: qubit_names[x] for x in range(num_qubits)},
|
|
106
|
+
font_size=6.5,
|
|
107
|
+
edgelist=qubit_pairs,
|
|
108
|
+
width=4.0,
|
|
109
|
+
edge_color=edge_colors,
|
|
110
|
+
node_color="k",
|
|
111
|
+
font_color="w",
|
|
112
|
+
ax=ax,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Add colorbar
|
|
116
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
|
|
117
|
+
sm.set_array([])
|
|
118
|
+
cbar = fig.colorbar(sm, ax=ax, shrink=0.5, label="Layered Fidelity (%)", format="%.2f")
|
|
119
|
+
cbar.set_ticks(np.linspace(min(fidelity_values), max(fidelity_values), 5, endpoint=True))
|
|
120
|
+
|
|
121
|
+
station_string = "IQM Backend" if station is None else station.capitalize()
|
|
122
|
+
|
|
123
|
+
eplg_string = (
|
|
124
|
+
f"EPLG estimate: {eplg_estimate['value']:.2e} +/- {eplg_estimate['uncertainty']:.2e}\n" if eplg_estimate else ""
|
|
125
|
+
)
|
|
126
|
+
plt.title(f"Layered fidelities for qubit pairs in {station_string}\n" f"{eplg_string}{timestamp}")
|
|
127
|
+
plt.close()
|
|
128
|
+
|
|
129
|
+
return fig_name, fig
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def eplg_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
|
|
133
|
+
"""EPLG analysis function
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
run (BenchmarkRunResult): The result of the benchmark run.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
AnalysisResult corresponding to DRB.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
result_direct_rb = direct_rb_analysis(run)
|
|
143
|
+
|
|
144
|
+
dataset = result_direct_rb.dataset.copy(deep=True)
|
|
145
|
+
observations = result_direct_rb.observations
|
|
146
|
+
plots: Dict[str, Figure] = {}
|
|
147
|
+
|
|
148
|
+
timestamp = dataset.attrs["execution_timestamp"]
|
|
149
|
+
|
|
150
|
+
backend_configuration_name = dataset.attrs["backend_configuration_name"]
|
|
151
|
+
backend_coupling_map = dataset.attrs["backend_coupling_map"]
|
|
152
|
+
backend_num_qubits = dataset.attrs["backend_num_qubits"]
|
|
153
|
+
|
|
154
|
+
num_edges = len(observations)
|
|
155
|
+
num_qubits = dataset.attrs["num_qubits"]
|
|
156
|
+
edges = dataset.attrs["edges"]
|
|
157
|
+
disjoint_layers = dataset.attrs["disjoint_layers"]
|
|
158
|
+
qubit_names = dataset.attrs["qubit_names"]
|
|
159
|
+
|
|
160
|
+
fidelities = {}
|
|
161
|
+
fid_product = ufloat(1, 0)
|
|
162
|
+
for obs in observations:
|
|
163
|
+
fid_product *= ufloat(obs.value, obs.uncertainty)
|
|
164
|
+
fidelities[str(obs.identifier.qubit_indices)] = {"value": obs.value, "uncertainty": obs.uncertainty}
|
|
165
|
+
|
|
166
|
+
LF = fid_product
|
|
167
|
+
EPLG = 1 - LF ** (1 / num_edges)
|
|
168
|
+
|
|
169
|
+
observations.append(
|
|
170
|
+
BenchmarkObservation(
|
|
171
|
+
name="layer_fidelity",
|
|
172
|
+
identifier=BenchmarkObservationIdentifier(f"(n_qubits={num_qubits})"),
|
|
173
|
+
value=LF.nominal_value,
|
|
174
|
+
uncertainty=LF.std_dev,
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
observations.append(
|
|
179
|
+
BenchmarkObservation(
|
|
180
|
+
name="eplg",
|
|
181
|
+
identifier=BenchmarkObservationIdentifier(f"(n_qubits={num_qubits})"),
|
|
182
|
+
value=EPLG.nominal_value,
|
|
183
|
+
uncertainty=EPLG.std_dev,
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Plot the edges graph
|
|
188
|
+
fig_name, fig = draw_graph_edges(
|
|
189
|
+
backend_coupling_map,
|
|
190
|
+
backend_num_qubits=backend_num_qubits,
|
|
191
|
+
edge_list=edges,
|
|
192
|
+
timestamp=timestamp,
|
|
193
|
+
disjoint_layers=disjoint_layers,
|
|
194
|
+
station=backend_configuration_name,
|
|
195
|
+
qubit_names=qubit_names,
|
|
196
|
+
is_eplg=True,
|
|
197
|
+
)
|
|
198
|
+
plots[fig_name] = fig
|
|
199
|
+
|
|
200
|
+
# Plot the layered fidelities graph
|
|
201
|
+
fig_name, fig = plot_layered_fidelities_graph(
|
|
202
|
+
fidelities=fidelities,
|
|
203
|
+
backend_coupling_map=backend_coupling_map,
|
|
204
|
+
qubit_names=qubit_names,
|
|
205
|
+
timestamp=timestamp,
|
|
206
|
+
station=backend_configuration_name,
|
|
207
|
+
eplg_estimate={"value": EPLG.nominal_value, "uncertainty": EPLG.std_dev},
|
|
208
|
+
)
|
|
209
|
+
plots[fig_name] = fig
|
|
210
|
+
|
|
211
|
+
plots.update(result_direct_rb.plots)
|
|
212
|
+
|
|
213
|
+
return BenchmarkAnalysisResult(dataset=dataset, observations=observations, plots=plots)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class EPLGBenchmark(Benchmark):
|
|
217
|
+
"""EPLG estimates the layer fidelity of native 2Q gate layers"""
|
|
218
|
+
|
|
219
|
+
analysis_function = staticmethod(eplg_analysis)
|
|
220
|
+
|
|
221
|
+
name: str = "EPLG"
|
|
222
|
+
|
|
223
|
+
def __init__(self, backend_arg: IQMBackendBase | str, configuration: "EPLGConfiguration"):
|
|
224
|
+
"""Construct the EPLG class
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
backend_arg (IQMBackendBase | str): The backend to use for the benchmark,
|
|
228
|
+
either as a backend instance or a backend name string.
|
|
229
|
+
configuration (EPLGConfiguration): The configuration settings for the EPLG benchmark.
|
|
230
|
+
"""
|
|
231
|
+
super().__init__(backend_arg, configuration)
|
|
232
|
+
# EXPERIMENT
|
|
233
|
+
self.backend_configuration_name = backend_arg if isinstance(backend_arg, str) else backend_arg.name
|
|
234
|
+
self.session_timestamp = strftime("%Y%m%d-%H%M%S")
|
|
235
|
+
self.execution_timestamp = ""
|
|
236
|
+
|
|
237
|
+
# Initialize the variable to contain the circuits for each layout
|
|
238
|
+
self.untranspiled_circuits = BenchmarkCircuit("untranspiled_circuits")
|
|
239
|
+
self.transpiled_circuits = BenchmarkCircuit("transpiled_circuits")
|
|
240
|
+
|
|
241
|
+
self.drb_depths = configuration.drb_depths
|
|
242
|
+
self.drb_circuit_samples = configuration.drb_circuit_samples
|
|
243
|
+
|
|
244
|
+
self.custom_qubits_array = configuration.custom_qubits_array
|
|
245
|
+
|
|
246
|
+
self.chain_length = configuration.chain_length
|
|
247
|
+
self.chain_path_samples = configuration.chain_path_samples
|
|
248
|
+
self.num_disjoint_layers = configuration.num_disjoint_layers
|
|
249
|
+
self.calibration_url = configuration.calibration_url
|
|
250
|
+
self.max_hamiltonian_path_tries = configuration.max_hamiltonian_path_tries
|
|
251
|
+
|
|
252
|
+
def add_all_meta_to_dataset(self, dataset: xr.Dataset):
|
|
253
|
+
"""Adds all configuration metadata and circuits to the dataset variable
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
dataset (xr.Dataset): The xarray dataset
|
|
257
|
+
"""
|
|
258
|
+
dataset.attrs["session_timestamp"] = self.session_timestamp
|
|
259
|
+
dataset.attrs["execution_timestamp"] = self.execution_timestamp
|
|
260
|
+
dataset.attrs["backend_configuration_name"] = self.backend_configuration_name
|
|
261
|
+
dataset.attrs["backend_name"] = self.backend.name
|
|
262
|
+
dataset.attrs["backend_coupling_map"] = self.backend.coupling_map
|
|
263
|
+
dataset.attrs["backend_num_qubits"] = self.backend.num_qubits
|
|
264
|
+
|
|
265
|
+
for key, value in self.configuration:
|
|
266
|
+
if key == "benchmark": # Avoid saving the class object
|
|
267
|
+
dataset.attrs[key] = value.name
|
|
268
|
+
else:
|
|
269
|
+
dataset.attrs[key] = value
|
|
270
|
+
# Defined outside configuration - if any
|
|
271
|
+
|
|
272
|
+
def validate_custom_qubits_array(self):
|
|
273
|
+
"""Validates the custom qubits array input ."""
|
|
274
|
+
if self.custom_qubits_array is not None:
|
|
275
|
+
# Validate that the custom qubits array is a list of pairs
|
|
276
|
+
if not all(isinstance(pair, tuple) and len(pair) == 2 for pair in self.custom_qubits_array):
|
|
277
|
+
raise ValueError("The custom qubits array must be a Sequence of tuples.")
|
|
278
|
+
# Validate that the custom qubits array has no repeated qubits
|
|
279
|
+
if len(set(tuple(sorted(x)) for x in self.custom_qubits_array)) != len(self.custom_qubits_array):
|
|
280
|
+
raise ValueError("The custom qubits array must have unique qubit pairs.")
|
|
281
|
+
|
|
282
|
+
def validate_random_chain_inputs(self):
|
|
283
|
+
"""Validates inputs for chain sampling.
|
|
284
|
+
|
|
285
|
+
Raises:
|
|
286
|
+
ValueError: If the chain inputs are beyond general or EPLG criteria.
|
|
287
|
+
"""
|
|
288
|
+
# Check chain length
|
|
289
|
+
if self.chain_length is None:
|
|
290
|
+
qcvv_logger.warning("chain_length input was None: will assign backend.num_qubits!")
|
|
291
|
+
self.chain_length = self.backend.num_qubits
|
|
292
|
+
elif self.chain_length > self.backend.num_qubits:
|
|
293
|
+
raise ValueError("The chain length cannot exceed the number of qubits in the backend.")
|
|
294
|
+
|
|
295
|
+
# Check path samples
|
|
296
|
+
if self.chain_path_samples is None:
|
|
297
|
+
self.chain_path_samples = 20
|
|
298
|
+
elif self.chain_path_samples < 1:
|
|
299
|
+
raise ValueError("The number of chain path samples must be a positive integer.")
|
|
300
|
+
|
|
301
|
+
# Check calibration URL - this is a temporary solution, normally the backend should be enough to specify this
|
|
302
|
+
if self.calibration_url is None:
|
|
303
|
+
raise ValueError("The calibration URL must be specified if custom qubits array is not specified.")
|
|
304
|
+
|
|
305
|
+
if self.num_disjoint_layers is None:
|
|
306
|
+
self.num_disjoint_layers = 2
|
|
307
|
+
elif self.num_disjoint_layers < 1:
|
|
308
|
+
raise ValueError("The number of disjoint layers must be a positive integer.")
|
|
309
|
+
|
|
310
|
+
if self.max_hamiltonian_path_tries is None:
|
|
311
|
+
self.max_hamiltonian_path_tries = 10
|
|
312
|
+
elif self.max_hamiltonian_path_tries < 1:
|
|
313
|
+
raise ValueError("The maximum number of Hamiltonian path tries must be a positive integer.")
|
|
314
|
+
|
|
315
|
+
def execute(self, backend: IQMBackendBase) -> xr.Dataset:
|
|
316
|
+
"""Execute the EPLG Benchmark"""
|
|
317
|
+
|
|
318
|
+
self.execution_timestamp = strftime("%Y%m%d-%H%M%S")
|
|
319
|
+
|
|
320
|
+
dataset_eplg = xr.Dataset()
|
|
321
|
+
|
|
322
|
+
if self.custom_qubits_array is not None:
|
|
323
|
+
self.validate_custom_qubits_array()
|
|
324
|
+
edges = self.custom_qubits_array
|
|
325
|
+
num_qubits = len(list(set(x for y in edges for x in y)))
|
|
326
|
+
all_disjoint = split_into_disjoint_pairs(self.custom_qubits_array)
|
|
327
|
+
self.num_disjoint_layers = len(all_disjoint)
|
|
328
|
+
qcvv_logger.info(
|
|
329
|
+
f"Using specified custom_qubits_array: will split into {self.num_disjoint_layers} disjoint layers."
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
else:
|
|
333
|
+
self.validate_random_chain_inputs()
|
|
334
|
+
num_qubits = cast(int, self.chain_length)
|
|
335
|
+
qcvv_logger.info("Generating linear chain path")
|
|
336
|
+
h_path_costs = evaluate_hamiltonian_paths(
|
|
337
|
+
self.chain_length,
|
|
338
|
+
self.chain_path_samples,
|
|
339
|
+
self.backend,
|
|
340
|
+
self.calibration_url,
|
|
341
|
+
self.max_hamiltonian_path_tries,
|
|
342
|
+
)
|
|
343
|
+
qcvv_logger.info("Extracting the path that maximizes total 2Q calibration fidelity")
|
|
344
|
+
max_cost_path = h_path_costs[max(h_path_costs.keys())]
|
|
345
|
+
|
|
346
|
+
all_disjoint = [
|
|
347
|
+
max_cost_path[i :: self.num_disjoint_layers] for i in range(cast(int, self.num_disjoint_layers))
|
|
348
|
+
]
|
|
349
|
+
edges = max_cost_path
|
|
350
|
+
|
|
351
|
+
dataset_eplg.attrs["num_qubits"] = num_qubits
|
|
352
|
+
backend_qubits = list(range(backend.num_qubits))
|
|
353
|
+
dataset_eplg.attrs["qubit_names"] = {qubit: self.backend.index_to_qubit_name(qubit) for qubit in backend_qubits}
|
|
354
|
+
|
|
355
|
+
self.add_all_meta_to_dataset(dataset_eplg)
|
|
356
|
+
|
|
357
|
+
# Execute parallel DRB in all disjoint layers
|
|
358
|
+
drb_config = DirectRBConfiguration(
|
|
359
|
+
qubits_array=all_disjoint,
|
|
360
|
+
is_eplg=True,
|
|
361
|
+
depths=self.drb_depths,
|
|
362
|
+
num_circuit_samples=self.drb_circuit_samples,
|
|
363
|
+
shots=self.shots,
|
|
364
|
+
max_gates_per_batch=self.max_gates_per_batch,
|
|
365
|
+
max_circuits_per_batch=self.configuration.max_circuits_per_batch,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
benchmarks_direct_rb = DirectRandomizedBenchmarking(backend, drb_config)
|
|
369
|
+
run_direct_rb = benchmarks_direct_rb.run()
|
|
370
|
+
dataset = run_direct_rb.dataset
|
|
371
|
+
self.circuits = benchmarks_direct_rb.circuits
|
|
372
|
+
dataset_eplg.attrs.update({"disjoint_layers": all_disjoint, "edges": edges})
|
|
373
|
+
dataset.attrs.update(dataset_eplg.attrs)
|
|
374
|
+
|
|
375
|
+
return dataset
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class EPLGConfiguration(BenchmarkConfigurationBase):
|
|
379
|
+
"""EPLG Configuration
|
|
380
|
+
|
|
381
|
+
Attributes:
|
|
382
|
+
drb_depths (Sequence[int]): The layer depths to consider for the parallel DRB.
|
|
383
|
+
drb_circuit_samples (int): The number of circuit samples to consider for the parallel DRB.
|
|
384
|
+
custom_qubits_array (Optional[Sequence[Tuple[int, int]]]): The custom qubits array to consider; this corresponds to a Sequence of Tuple pairs of qubits.
|
|
385
|
+
* If not specified, will proceed to generate linear chains at random, selecting the one with the highest total 2Q gate fidelity.
|
|
386
|
+
* Default is None.
|
|
387
|
+
chain_length (Optional[int]): The length of a linear chain of 2Q gates to consider, corresponding to the number of qubits, if custom_qubits_array not specified.
|
|
388
|
+
* Default is None: assigns the number of qubits in the backend minus one.
|
|
389
|
+
chain_path_samples (int): The number of chain path samples to consider, if custom_qubits_array not specified.
|
|
390
|
+
* Default is None: assigns 20 path samples (arbitrary).
|
|
391
|
+
num_disjoint_layers (Optional[int]): The number of disjoint layers to consider.
|
|
392
|
+
* Default is None: assigns 2 disjoint layers (arbitrary).
|
|
393
|
+
max_hamiltonian_path_tries (Optional[int]): The maximum number of tries to find a Hamiltonian path.
|
|
394
|
+
* Default is None: assigns 10 tries (arbitrary).
|
|
395
|
+
calibration_url (Optional[str]): The URL of the IQM station to retrieve calibration data from.
|
|
396
|
+
* It must be specified if custom_qubits_array is not specified.
|
|
397
|
+
* Default is None - raises an error if custom_qubits_array is not specified.
|
|
398
|
+
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
benchmark: Type[Benchmark] = EPLGBenchmark
|
|
402
|
+
drb_depths: Sequence[int]
|
|
403
|
+
drb_circuit_samples: int
|
|
404
|
+
custom_qubits_array: Optional[Sequence[Tuple[int, int]]] = None
|
|
405
|
+
chain_length: Optional[int] = None
|
|
406
|
+
chain_path_samples: Optional[int] = None
|
|
407
|
+
num_disjoint_layers: Optional[int] = None
|
|
408
|
+
max_hamiltonian_path_tries: Optional[int] = None
|
|
409
|
+
calibration_url: Optional[str] = None
|