iqm-benchmarks 2.43__py3-none-any.whl → 2.45__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/benchmark_definition.py +1 -0
- iqm/benchmarks/compressive_gst/compressive_gst.py +100 -46
- iqm/benchmarks/compressive_gst/gst_analysis.py +480 -381
- iqm/benchmarks/entanglement/ghz.py +5 -5
- iqm/benchmarks/entanglement/graph_states.py +21 -21
- iqm/benchmarks/optimization/qscore.py +2 -2
- iqm/benchmarks/quantum_volume/clops.py +2 -3
- iqm/benchmarks/randomized_benchmarking/eplg/eplg.py +23 -24
- iqm/benchmarks/utils.py +72 -8
- iqm/benchmarks/utils_plots.py +309 -65
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/METADATA +2 -2
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/RECORD +22 -22
- mGST/additional_fns.py +35 -20
- mGST/algorithm.py +73 -57
- mGST/low_level_jit.py +122 -56
- mGST/optimization.py +12 -6
- mGST/qiskit_interface.py +64 -87
- mGST/reporting/figure_gen.py +390 -57
- mGST/reporting/reporting.py +209 -11
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/top_level.txt +0 -0
iqm/benchmarks/utils_plots.py
CHANGED
|
@@ -17,9 +17,10 @@ Plotting and visualization utility functions
|
|
|
17
17
|
"""
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
import os
|
|
20
|
-
from typing import Dict, List, Literal, Optional, Sequence, Tuple
|
|
20
|
+
from typing import Dict, List, Literal, Optional, Sequence, Tuple, cast
|
|
21
21
|
|
|
22
22
|
from matplotlib.figure import Figure
|
|
23
|
+
from matplotlib.patches import Circle, FancyBboxPatch
|
|
23
24
|
import matplotlib.pyplot as plt
|
|
24
25
|
import networkx as nx
|
|
25
26
|
import numpy as np
|
|
@@ -40,9 +41,9 @@ class GraphPositions:
|
|
|
40
41
|
provides methods to generate positions for different layout types.
|
|
41
42
|
|
|
42
43
|
Attributes:
|
|
43
|
-
garnet_positions (Dict[int, Tuple[
|
|
44
|
-
deneb_positions (Dict[int, Tuple[
|
|
45
|
-
predefined_stations (Dict[str, Dict[int, Tuple[
|
|
44
|
+
garnet_positions (Dict[int, Tuple[float, float]]): Mapping of node indices to (x,y) positions for Garnet chip.
|
|
45
|
+
deneb_positions (Dict[int, Tuple[float, float]]): Mapping of node indices to (x,y) positions for Deneb chip.
|
|
46
|
+
predefined_stations (Dict[str, Dict[int, Tuple[float, float]]]): Mapping of chip names to their position dictionaries.
|
|
46
47
|
"""
|
|
47
48
|
|
|
48
49
|
garnet_positions = {
|
|
@@ -68,15 +69,116 @@ class GraphPositions:
|
|
|
68
69
|
19: (3.0, 1.0),
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
emerald_positions = {
|
|
73
|
+
0: (10, 10),
|
|
74
|
+
1: (11, 9),
|
|
75
|
+
2: (7, 11),
|
|
76
|
+
3: (8, 10),
|
|
77
|
+
4: (9, 9),
|
|
78
|
+
5: (10, 8),
|
|
79
|
+
6: (11, 7),
|
|
80
|
+
7: (5, 11),
|
|
81
|
+
8: (6, 10),
|
|
82
|
+
9: (7, 9),
|
|
83
|
+
10: (8, 8),
|
|
84
|
+
11: (9, 7),
|
|
85
|
+
12: (10, 6),
|
|
86
|
+
13: (11, 5),
|
|
87
|
+
14: (3, 11),
|
|
88
|
+
15: (4, 10),
|
|
89
|
+
16: (5, 9),
|
|
90
|
+
17: (6, 8),
|
|
91
|
+
18: (7, 7),
|
|
92
|
+
19: (8, 6),
|
|
93
|
+
20: (9, 5),
|
|
94
|
+
21: (10, 4),
|
|
95
|
+
22: (2, 10),
|
|
96
|
+
23: (3, 9),
|
|
97
|
+
24: (4, 8),
|
|
98
|
+
25: (5, 7),
|
|
99
|
+
26: (6, 6),
|
|
100
|
+
27: (7, 5),
|
|
101
|
+
28: (8, 4),
|
|
102
|
+
29: (9, 3),
|
|
103
|
+
30: (10, 2),
|
|
104
|
+
31: (2, 8),
|
|
105
|
+
32: (3, 7),
|
|
106
|
+
33: (4, 6),
|
|
107
|
+
34: (5, 5),
|
|
108
|
+
35: (6, 4),
|
|
109
|
+
36: (7, 3),
|
|
110
|
+
37: (8, 2),
|
|
111
|
+
38: (9, 1),
|
|
112
|
+
39: (1, 7),
|
|
113
|
+
40: (2, 6),
|
|
114
|
+
41: (3, 5),
|
|
115
|
+
42: (4, 4),
|
|
116
|
+
43: (5, 3),
|
|
117
|
+
44: (6, 2),
|
|
118
|
+
45: (7, 1),
|
|
119
|
+
46: (1, 5),
|
|
120
|
+
47: (2, 4),
|
|
121
|
+
48: (3, 3),
|
|
122
|
+
49: (4, 2),
|
|
123
|
+
50: (5, 1),
|
|
124
|
+
51: (1, 3),
|
|
125
|
+
52: (2, 2),
|
|
126
|
+
53: (3, 1),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
sirius_positions = {
|
|
130
|
+
# Even nodes on the bottom
|
|
131
|
+
2: (1, 5),
|
|
132
|
+
4: (3, 5),
|
|
133
|
+
6: (5, 5),
|
|
134
|
+
8: (7, 5),
|
|
135
|
+
10: (9, 5),
|
|
136
|
+
12: (11, 5),
|
|
137
|
+
14: (13, 5),
|
|
138
|
+
16: (15, 5),
|
|
139
|
+
18: (17, 5),
|
|
140
|
+
20: (19, 5),
|
|
141
|
+
22: (21, 5),
|
|
142
|
+
24: (23, 5),
|
|
143
|
+
# Odd nodes on the top
|
|
144
|
+
1: (1, 1),
|
|
145
|
+
3: (3, 1),
|
|
146
|
+
5: (5, 1),
|
|
147
|
+
7: (7, 1),
|
|
148
|
+
9: (9, 1),
|
|
149
|
+
11: (11, 1),
|
|
150
|
+
13: (13, 1),
|
|
151
|
+
15: (15, 1),
|
|
152
|
+
17: (17, 1),
|
|
153
|
+
19: (19, 1),
|
|
154
|
+
21: (21, 1),
|
|
155
|
+
23: (23, 1),
|
|
156
|
+
}
|
|
157
|
+
# Add dummy indices for the resonator
|
|
158
|
+
max_qubit_number = np.max(list(sirius_positions.keys()))
|
|
159
|
+
previous_nodes = list(sirius_positions.keys())
|
|
160
|
+
for node in previous_nodes:
|
|
161
|
+
sirius_positions.update({node + max_qubit_number: (sirius_positions[node][0], 3)})
|
|
162
|
+
# Node 0 in the middle
|
|
163
|
+
sirius_positions.update({0: (16.5, 3)})
|
|
164
|
+
|
|
71
165
|
deneb_positions = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
166
|
+
# Even nodes on the bottom
|
|
167
|
+
2: (1, 5),
|
|
168
|
+
4: (3, 5),
|
|
169
|
+
6: (5, 5),
|
|
170
|
+
# Odd nodes on the top
|
|
171
|
+
1: (1, 1),
|
|
172
|
+
3: (3, 1),
|
|
173
|
+
5: (5, 1),
|
|
79
174
|
}
|
|
175
|
+
# Add dummy indices for the resonator
|
|
176
|
+
max_qubit_number = np.max(list(deneb_positions.keys()))
|
|
177
|
+
previous_nodes = list(deneb_positions.keys())
|
|
178
|
+
for node in previous_nodes:
|
|
179
|
+
deneb_positions.update({node + max_qubit_number: (deneb_positions[node][0], 3)})
|
|
180
|
+
# Node 0 in the middle
|
|
181
|
+
deneb_positions.update({0: (2.5, 3)})
|
|
80
182
|
|
|
81
183
|
predefined_stations = {
|
|
82
184
|
"garnet": garnet_positions,
|
|
@@ -85,6 +187,8 @@ class GraphPositions:
|
|
|
85
187
|
"deneb": deneb_positions,
|
|
86
188
|
"fakedeneb": deneb_positions,
|
|
87
189
|
"iqmfakedeneb": deneb_positions,
|
|
190
|
+
"emerald": emerald_positions,
|
|
191
|
+
"sirius": sirius_positions,
|
|
88
192
|
}
|
|
89
193
|
|
|
90
194
|
@staticmethod
|
|
@@ -128,6 +232,40 @@ class GraphPositions:
|
|
|
128
232
|
}
|
|
129
233
|
return pos
|
|
130
234
|
|
|
235
|
+
@staticmethod
|
|
236
|
+
def get_positions(
|
|
237
|
+
station: Optional[str] = None, graph: Optional[PyGraph] = None, num_qubits: Optional[int] = None
|
|
238
|
+
) -> Dict[int, Tuple[float, float]]:
|
|
239
|
+
"""Get predefined positions for a specific station or generate positions for a custom graph.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
station (Optional[str]): The name of the station to get predefined positions for.
|
|
243
|
+
If None, positions will be generated algorithmically.
|
|
244
|
+
graph (Optional[PyGraph]): The graph to generate positions for if no predefined positions exist.
|
|
245
|
+
Used only when station is None and num_qubits doesn't match any predefined layout.
|
|
246
|
+
num_qubits (Optional[int]): The number of qubits to get a layout for.
|
|
247
|
+
If matches a known system, predefined positions will be used.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Dict[int, Tuple[float, float]]: A dictionary mapping node indices to (x,y) coordinates.
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
ValueError: If none of station, graph, or num_qubits are provided, or if num_qubits doesn't
|
|
254
|
+
match any predefined layout and graph is None.
|
|
255
|
+
"""
|
|
256
|
+
if station is not None and station.lower() in GraphPositions.predefined_stations:
|
|
257
|
+
qubit_positions = cast(Dict[int, Tuple[float, float]], GraphPositions.predefined_stations[station.lower()])
|
|
258
|
+
else:
|
|
259
|
+
qubit_station_dict = {6: "deneb", 7: "deneb", 20: "garnet", 24: "sirius", 17: "sirius", 54: "emerald"}
|
|
260
|
+
if num_qubits is not None and num_qubits in qubit_station_dict:
|
|
261
|
+
station = qubit_station_dict[num_qubits]
|
|
262
|
+
qubit_positions = cast(Dict[int, Tuple[float, float]], GraphPositions.predefined_stations[station])
|
|
263
|
+
elif graph is not None:
|
|
264
|
+
qubit_positions = GraphPositions.create_positions(graph)
|
|
265
|
+
else:
|
|
266
|
+
raise ValueError("Either a station name, a graph, or a qubit count must be provided to get positions.")
|
|
267
|
+
return qubit_positions
|
|
268
|
+
|
|
131
269
|
|
|
132
270
|
def draw_graph_edges(
|
|
133
271
|
backend_coupling_map: CouplingMap,
|
|
@@ -165,23 +303,7 @@ def draw_graph_edges(
|
|
|
165
303
|
fig = plt.figure()
|
|
166
304
|
ax = plt.axes()
|
|
167
305
|
|
|
168
|
-
|
|
169
|
-
if station.lower() in GraphPositions.predefined_stations:
|
|
170
|
-
qubit_positions = GraphPositions.predefined_stations[station.lower()]
|
|
171
|
-
else:
|
|
172
|
-
if backend_num_qubits in (6, 20):
|
|
173
|
-
station = "garnet" if backend_num_qubits == 20 else "deneb"
|
|
174
|
-
qubit_positions = GraphPositions.predefined_stations[station]
|
|
175
|
-
else:
|
|
176
|
-
graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
|
|
177
|
-
qubit_positions = GraphPositions.create_positions(graph_backend)
|
|
178
|
-
else:
|
|
179
|
-
graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
|
|
180
|
-
if backend_num_qubits in (6, 20):
|
|
181
|
-
station = "garnet" if backend_num_qubits == 20 else "deneb"
|
|
182
|
-
qubit_positions = GraphPositions.predefined_stations[station]
|
|
183
|
-
else:
|
|
184
|
-
qubit_positions = GraphPositions.create_positions(graph_backend)
|
|
306
|
+
qubit_positions = GraphPositions.get_positions(station=station, graph=None, num_qubits=backend_num_qubits)
|
|
185
307
|
|
|
186
308
|
label_station = station if station is not None else f"{backend_num_qubits}-qubit IQM Backend"
|
|
187
309
|
if disjoint_layers is None:
|
|
@@ -226,6 +348,7 @@ def draw_graph_edges(
|
|
|
226
348
|
f"\n{timestamp}"
|
|
227
349
|
)
|
|
228
350
|
ax.set_aspect(0.925)
|
|
351
|
+
plt.gca().invert_yaxis()
|
|
229
352
|
plt.close()
|
|
230
353
|
|
|
231
354
|
return fig_name, fig
|
|
@@ -302,8 +425,55 @@ def evaluate_hamiltonian_paths(
|
|
|
302
425
|
return path_costs
|
|
303
426
|
|
|
304
427
|
|
|
428
|
+
def calculate_node_radii(metric_dict: Dict[str, Dict[int, float]], qubit_nodes: List[int], sq_metric: str) -> np.ndarray:
|
|
429
|
+
"""Calculate node radii based on the specified single qubit metric. For the coherence metric, the fidelity is calculated as the idling fidelity of a single qubit gate duration.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
metric_dict (Dict[str, Dict[int, float]]): Dictionary containing various qubit metrics.
|
|
433
|
+
qubit_nodes (List[int]): List of qubits to calculate the radius for.
|
|
434
|
+
sq_metric (str): Metric to use for radius calculation.
|
|
435
|
+
Options: "fidelity", "coherence", or "readout".
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
numpy.ndarray: Array of radii values for each qubit node.
|
|
439
|
+
|
|
440
|
+
Raises:
|
|
441
|
+
ValueError: If an unsupported metric type is provided.
|
|
442
|
+
"""
|
|
443
|
+
if sq_metric == "fidelity":
|
|
444
|
+
radii = -np.log(np.array([metric_dict["fidelity_1qb_gates_averaged"][node] for node in qubit_nodes]))
|
|
445
|
+
if "fidelity_1qb_gates_averaged" not in metric_dict:
|
|
446
|
+
raise ValueError(
|
|
447
|
+
"The metric 'fidelity_1qb_gates_averaged' is not available in the backend metrics."
|
|
448
|
+
)
|
|
449
|
+
elif sq_metric == "coherence":
|
|
450
|
+
if "t1_time" not in metric_dict or "t2_time" not in metric_dict:
|
|
451
|
+
raise ValueError(
|
|
452
|
+
"At least one of the metrics 't1_time' and 't2_time' is not available in the backend metrics."
|
|
453
|
+
)
|
|
454
|
+
sqg_time = 32e-9
|
|
455
|
+
t1_times = [metric_dict["t1_time"][node] for node in qubit_nodes]
|
|
456
|
+
t2_times = [metric_dict["t2_time"][node] for node in qubit_nodes]
|
|
457
|
+
idle_fidelities = (3 + np.exp(-sqg_time / np.array(t1_times)) + 2 * np.exp(-sqg_time / np.array(t2_times))) / 6
|
|
458
|
+
radii = -np.log(idle_fidelities)
|
|
459
|
+
elif sq_metric == "readout":
|
|
460
|
+
if "single_shot_readout_fidelity" not in metric_dict:
|
|
461
|
+
raise ValueError(
|
|
462
|
+
"The metric 'single_shot_readout_fidelity' is both available in the backend metrics."
|
|
463
|
+
)
|
|
464
|
+
readout_fidelities = [metric_dict["single_shot_readout_fidelity"][node] for node in qubit_nodes]
|
|
465
|
+
radii = -np.log(readout_fidelities)
|
|
466
|
+
else:
|
|
467
|
+
raise ValueError(
|
|
468
|
+
f"Unsupported single qubit metric: {sq_metric}, supported metrics are: fidelity, coherence, readout"
|
|
469
|
+
)
|
|
470
|
+
return radii
|
|
471
|
+
|
|
305
472
|
def plot_layout_fidelity_graph(
|
|
306
|
-
cal_url: str,
|
|
473
|
+
cal_url: str,
|
|
474
|
+
qubit_layouts: Optional[list[list[int]]] = None,
|
|
475
|
+
station: Optional[str] = None,
|
|
476
|
+
sq_metric: Optional[str] = "coherence",
|
|
307
477
|
):
|
|
308
478
|
"""Plot a graph showing the quantum chip layout with fidelity information.
|
|
309
479
|
|
|
@@ -316,57 +486,75 @@ def plot_layout_fidelity_graph(
|
|
|
316
486
|
qubit_layouts: List of qubit layouts where each layout is a list of qubit indices
|
|
317
487
|
station: Name of the quantum computing station to use predefined positions for.
|
|
318
488
|
If None, positions will be generated algorithmically.
|
|
489
|
+
sq_metric: Optional single qubit metric to use for the visualization, can be either "fidelity", "coherence",
|
|
490
|
+
or "readout".
|
|
319
491
|
|
|
320
492
|
Returns:
|
|
321
493
|
matplotlib.figure.Figure: The generated figure object containing the graph visualization
|
|
322
494
|
"""
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
495
|
+
# pylint: disable=unbalanced-tuple-unpacking, disable=too-many-statements
|
|
496
|
+
edges_cal, fidelities_cal, topology, qubit_mapping, metric_dict = extract_fidelities(cal_url, all_metrics=True)
|
|
497
|
+
if topology == "star":
|
|
498
|
+
idx_to_qubit = {idx: qubit for qubit, idx in qubit_mapping.items()}
|
|
499
|
+
qubit_nodes = list(idx_to_qubit.keys())[1:]
|
|
500
|
+
fig, ax = plt.subplots(figsize=(len(qubit_nodes), 3))
|
|
501
|
+
else:
|
|
502
|
+
# For other topologies, qubits are indexed starting from 0 as per the Qiskit convention
|
|
503
|
+
idx_to_qubit = {idx: qubit - 1 for qubit, idx in qubit_mapping.items()}
|
|
504
|
+
qubit_nodes = list(idx_to_qubit.keys())
|
|
505
|
+
fig, ax = plt.subplots(figsize=(1.5 * np.sqrt(len(qubit_nodes)), 1.5 * np.sqrt(len(qubit_nodes))))
|
|
334
506
|
|
|
335
|
-
|
|
336
|
-
|
|
507
|
+
weights = -np.log(np.array(fidelities_cal))
|
|
508
|
+
calibrated_nodes = list(idx_to_qubit.keys())
|
|
337
509
|
|
|
338
510
|
# Define qubit positions in plot
|
|
339
|
-
|
|
340
|
-
pos = GraphPositions.predefined_stations[station.lower()]
|
|
341
|
-
else:
|
|
342
|
-
pos = GraphPositions.create_positions(graph, topology)
|
|
511
|
+
qubit_positions = GraphPositions.get_positions(station=station, graph=None, num_qubits=len(calibrated_nodes))
|
|
343
512
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
513
|
+
graph = PyGraph()
|
|
514
|
+
nodes = list(set(qubit_positions.keys()))
|
|
515
|
+
graph.add_nodes_from(nodes)
|
|
516
|
+
for edge, weight in zip(edges_cal, weights):
|
|
517
|
+
if topology == "star":
|
|
518
|
+
max_qubit_number = (np.max(list(qubit_positions.keys())) + 1) // 2
|
|
519
|
+
graph.add_edge(idx_to_qubit[edge[0]], idx_to_qubit[edge[0]] + max_qubit_number, weight)
|
|
520
|
+
else:
|
|
521
|
+
graph.add_edge(idx_to_qubit[edge[0]], idx_to_qubit[edge[1]], weight)
|
|
351
522
|
|
|
352
|
-
# Draw the graph
|
|
523
|
+
# Draw the main graph
|
|
353
524
|
visualization.mpl_draw(
|
|
354
525
|
graph,
|
|
526
|
+
ax=ax,
|
|
355
527
|
with_labels=True,
|
|
356
|
-
node_color=
|
|
357
|
-
pos=
|
|
528
|
+
node_color="none", # No node color since we're using circles
|
|
529
|
+
pos=qubit_positions,
|
|
358
530
|
labels=lambda node: node,
|
|
359
|
-
|
|
531
|
+
font_color="white",
|
|
532
|
+
width=graph.edges() / np.max(graph.edges()) * 10,
|
|
360
533
|
) # type: ignore[call-arg]
|
|
361
534
|
|
|
535
|
+
# Draw nodes as circles with varying radii given by the single qubit metric
|
|
536
|
+
radii = calculate_node_radii(metric_dict, qubit_nodes, sq_metric)
|
|
537
|
+
node_colors = ["darkgray" for _ in range(len(nodes))]
|
|
538
|
+
if qubit_layouts is not None:
|
|
539
|
+
for qb in {qb for layout in qubit_layouts for qb in layout}:
|
|
540
|
+
node_colors[qb] = "orange"
|
|
541
|
+
max_radius = 0.12 + np.max(radii) / np.max(radii) / 2.5
|
|
542
|
+
|
|
543
|
+
for idx, node in enumerate(qubit_nodes):
|
|
544
|
+
position = qubit_positions[idx_to_qubit[node]]
|
|
545
|
+
radius = 0.12 + radii[idx] / np.max(radii) / 2.5
|
|
546
|
+
circle = Circle(position, radius=radius, color=node_colors[idx_to_qubit[node]], fill=True, alpha=1)
|
|
547
|
+
ax.add_patch(circle)
|
|
548
|
+
|
|
362
549
|
# Add edge labels using matplotlib's annotate
|
|
363
|
-
for edge in
|
|
364
|
-
|
|
365
|
-
|
|
550
|
+
# for idx, edge in enumerate(edges_cal):
|
|
551
|
+
for edge, weight in zip(list(graph.edge_list()), graph.edges()):
|
|
552
|
+
x1, y1 = qubit_positions[edge[0]]
|
|
553
|
+
x2, y2 = qubit_positions[edge[1]]
|
|
366
554
|
x = (x1 + x2) / 2
|
|
367
555
|
y = (y1 + y2) / 2
|
|
368
556
|
plt.annotate(
|
|
369
|
-
f"{
|
|
557
|
+
f"{weight:.1e}",
|
|
370
558
|
xy=(x, y),
|
|
371
559
|
xytext=(0, 0),
|
|
372
560
|
textcoords="offset points",
|
|
@@ -375,12 +563,68 @@ def plot_layout_fidelity_graph(
|
|
|
375
563
|
bbox={"boxstyle": "round,pad=0.2", "fc": "white", "ec": "none", "alpha": 0.6},
|
|
376
564
|
)
|
|
377
565
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
566
|
+
# Add horizontal bar representing resonator
|
|
567
|
+
if topology == "star":
|
|
568
|
+
resonator_height = 3
|
|
569
|
+
resonator_thickness = 0.8
|
|
570
|
+
x_min = 0.5
|
|
571
|
+
x_max = qubit_positions[idx_to_qubit[qubit_nodes[-1]]][0] + 0.5
|
|
572
|
+
resonator_width = x_max - x_min
|
|
573
|
+
|
|
574
|
+
# Create rectangle with rounded corners
|
|
575
|
+
resonator = FancyBboxPatch(
|
|
576
|
+
(x_min, resonator_height - resonator_thickness / 2),
|
|
577
|
+
resonator_width,
|
|
578
|
+
resonator_thickness,
|
|
579
|
+
boxstyle="round,pad=0.01,rounding_size=0.3",
|
|
580
|
+
color="lightsteelblue",
|
|
581
|
+
zorder=10,
|
|
582
|
+
)
|
|
583
|
+
ax.add_patch(resonator)
|
|
584
|
+
|
|
585
|
+
# Add "Resonator" label in the center
|
|
586
|
+
plt.annotate(
|
|
587
|
+
"Resonator",
|
|
588
|
+
xy=((x_min + x_max) / 2, resonator_height),
|
|
589
|
+
xytext=(0, 0),
|
|
590
|
+
textcoords="offset points",
|
|
591
|
+
ha="center",
|
|
592
|
+
va="center",
|
|
593
|
+
color="black",
|
|
594
|
+
fontsize=10,
|
|
595
|
+
zorder=11,
|
|
596
|
+
bbox={"boxstyle": "round,pad=0.2", "fc": "white", "ec": "none", "alpha": 0.8},
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
# Calculate axis limits to ensure all circles are visible
|
|
600
|
+
all_x = [pos[0] for pos in qubit_positions.values()]
|
|
601
|
+
all_y = [pos[1] for pos in qubit_positions.values()]
|
|
602
|
+
x_min, x_max = min(all_x), max(all_x)
|
|
603
|
+
y_min, y_max = min(all_y), max(all_y)
|
|
604
|
+
|
|
605
|
+
# Add padding for circles
|
|
606
|
+
padding = max_radius * 1.5
|
|
607
|
+
ax.set_xlim(x_min - padding, x_max + padding)
|
|
608
|
+
ax.set_ylim(y_min - padding, y_max + padding)
|
|
609
|
+
|
|
610
|
+
# Adjust layout first
|
|
611
|
+
plt.tight_layout(pad=2.0)
|
|
612
|
+
ax.set_aspect("equal")
|
|
613
|
+
ax.invert_yaxis()
|
|
614
|
+
|
|
615
|
+
plt.figtext(
|
|
616
|
+
0.5,
|
|
617
|
+
0.99, # x=0.5 (center), y=0.01 (bottom)
|
|
618
|
+
f"Qubit connectivity with selected qubits in orange\n"
|
|
619
|
+
f"CZ errors -log(F) indicated by edge thickness (thinner is better)\n"
|
|
620
|
+
f"Single qubit errors -log(F) shown as node size with F computed from {sq_metric} metrics",
|
|
621
|
+
fontsize=10,
|
|
622
|
+
ha="center",
|
|
623
|
+
wrap=True,
|
|
382
624
|
)
|
|
625
|
+
|
|
383
626
|
plt.show()
|
|
627
|
+
return fig
|
|
384
628
|
|
|
385
629
|
|
|
386
630
|
def rx_to_nx_graph(backend_coupling_map: CouplingMap) -> nx.Graph:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iqm-benchmarks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.45
|
|
4
4
|
Summary: A package for implementation of Quantum Characterization, Verification and Validation (QCVV) techniques on IQM's hardware at gate level abstraction
|
|
5
5
|
Author-email: IQM Finland Oy <developers@meetiqm.com>, Adrian Auer <adrian.auer@meetiqm.com>, Raphael Brieger <raphael.brieger@meetiqm.com>, Alessio Calzona <alessio.calzona@meetiqm.com>, Pedro Figueroa Romero <pedro.romero@meetiqm.com>, Amin Hosseinkhani <amin.hosseinkhani@meetiqm.com>, Miikka Koistinen <miikka@meetiqm.com>, Nadia Milazzo <nadia.milazzo@meetiqm.com>, Vicente Pina Canelles <vicente.pina@meetiqm.com>, Aniket Rath <aniket.rath@meetiqm.com>, Jami Rönkkö <jami@meetiqm.com>, Stefan Seegerer <stefan.seegerer@meetiqm.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/iqm-finland/iqm-benchmarks
|
|
@@ -63,7 +63,7 @@ Below is a list of the benchmarks currently available in the suite:
|
|
|
63
63
|
* Gates / Layers:
|
|
64
64
|
- Standard Clifford Randomized Benchmarking [[Phys. Rev. A 85, 042311](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.85.042311) (2012)]
|
|
65
65
|
- Interleaved Randomized Benchmarking [[Phys. Rev. Lett. 109, 080505](https://doi.org/10.1103/PhysRevLett.109.080505) (2012)]
|
|
66
|
-
- Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)]
|
|
66
|
+
- Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)] (Optional dependencies required)
|
|
67
67
|
- Mirror Randomized Benchmarking [[Phys. Rev. Lett. 129, 150502](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.129.150502) (2022)]
|
|
68
68
|
- Error Per Layered Gate [[arXiv:2311.05933 [quant-ph]](https://arxiv.org/abs/2311.05933) (2023)]
|
|
69
69
|
* Holistic:
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
iqm/benchmarks/__init__.py,sha256=-drZ_whue067Tu4I9RxBCqrrzu38Tm5Kqf9jHTftUPk,3070
|
|
2
2
|
iqm/benchmarks/benchmark.py,sha256=3E7g7RQjCIGIpSI1gOSrI3V9SAVs-XEOMrPgToK_7vw,4972
|
|
3
|
-
iqm/benchmarks/benchmark_definition.py,sha256=
|
|
3
|
+
iqm/benchmarks/benchmark_definition.py,sha256=7sq9_Y0iu1ZJOjG0gAVpm_BK3hbJBa6bdcRKhcVFOJw,11321
|
|
4
4
|
iqm/benchmarks/circuit_containers.py,sha256=anEtZEsodYqOX-34oZRmuKGeEpp_VfgG5045Mz4-4hI,7562
|
|
5
5
|
iqm/benchmarks/logging_config.py,sha256=U7olP5Kr75AcLJqNODf9VBhJLVqIvA4AYR6J39D5rww,1052
|
|
6
6
|
iqm/benchmarks/readout_mitigation.py,sha256=Q2SOGWTNgmklOYkNxepAaSaXlxSj0QQyymYY1bOkT8A,11756
|
|
7
|
-
iqm/benchmarks/utils.py,sha256=
|
|
8
|
-
iqm/benchmarks/utils_plots.py,sha256=
|
|
7
|
+
iqm/benchmarks/utils.py,sha256=kJz9T9nJXpLl_iFYYUDtSq83N-Y3JFQBuvW1o7-AVSM,44137
|
|
8
|
+
iqm/benchmarks/utils_plots.py,sha256=CaqA9fJNgRnrbYqwBdpzFUlhwvKw5lhZX3KfRlroQV4,24420
|
|
9
9
|
iqm/benchmarks/utils_shadows.py,sha256=e77PV_uaAO5m_woox9lAzompKAvFeDJ-0AKJrNJ7NFg,9728
|
|
10
10
|
iqm/benchmarks/coherence/__init__.py,sha256=yeyhk-_Lp8IbJ-f5lQj0HP5Q1HSKK_FzuXHazotUrVY,704
|
|
11
11
|
iqm/benchmarks/coherence/coherence.py,sha256=zX_6A8vCS2zeWesMDXPFZBfrJ8wUG90JI9_tFsonwXk,21191
|
|
12
12
|
iqm/benchmarks/compressive_gst/__init__.py,sha256=LneifgYXtcwo2jcXo7GdUEHL6_peipukShhkrdaTRCA,929
|
|
13
|
-
iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=
|
|
14
|
-
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=
|
|
13
|
+
iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=c33PUisGlmPUkt4Nx2Xqmduji5qFCrWT9jMcoThcs7U,27992
|
|
14
|
+
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=H6EQGbpI_sig69Jy6hflg6alMTtjB0t9tHftygzA2YA,41240
|
|
15
15
|
iqm/benchmarks/entanglement/__init__.py,sha256=sHVVToRWRCz0LSntk1rQaoSNNeyZLPoiTjUKWZWrk1E,778
|
|
16
|
-
iqm/benchmarks/entanglement/ghz.py,sha256=
|
|
17
|
-
iqm/benchmarks/entanglement/graph_states.py,sha256=
|
|
16
|
+
iqm/benchmarks/entanglement/ghz.py,sha256=7QI7r9x5VEYQu5kqmdsLV9nq3eedYV_WRVqCQTbjapk,41294
|
|
17
|
+
iqm/benchmarks/entanglement/graph_states.py,sha256=saoAw2QF8j7W4nZMOElnjuNylqDAbY9cBwBypWZKUz8,62521
|
|
18
18
|
iqm/benchmarks/optimization/__init__.py,sha256=_ajW_OibYLCtzU5AUv5c2zuuVYn8ZNeZUcUUSIGt51M,747
|
|
19
|
-
iqm/benchmarks/optimization/qscore.py,sha256=
|
|
19
|
+
iqm/benchmarks/optimization/qscore.py,sha256=hh96ZVYU7rUYCBEQyhFZNv0IOQoAwM5RYfRvqRl05ow,37281
|
|
20
20
|
iqm/benchmarks/quantum_volume/__init__.py,sha256=i-Q4SpDWELBw7frXnxm1j4wJRcxbIyrS5uEK_v06YHo,951
|
|
21
|
-
iqm/benchmarks/quantum_volume/clops.py,sha256=
|
|
21
|
+
iqm/benchmarks/quantum_volume/clops.py,sha256=QS9iK-gtop0zix6IBeUumQeG01-0dXsv0jsYSDhgEu0,31071
|
|
22
22
|
iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=pro7Lz-A5pPpT9UZ8wtXKTyhdWmTjQjRHt4BylDR-3U,36553
|
|
23
23
|
iqm/benchmarks/randomized_benchmarking/__init__.py,sha256=IkKo-7zUChxZZd3my_csQCJfJfZNsV3-JTvdG8uqys4,734
|
|
24
24
|
iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl,sha256=yrmSJqhv7Lb1yqiqU9-2baqTljJPNmTUPQR-AH6GGfc,7800
|
|
@@ -30,23 +30,23 @@ iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py,sha256=IGBrq_a
|
|
|
30
30
|
iqm/benchmarks/randomized_benchmarking/direct_rb/__init__.py,sha256=lCIIeWMFZHnMUUEUTjUBvrhhUur6uBTHIVkxFBSfHC4,681
|
|
31
31
|
iqm/benchmarks/randomized_benchmarking/direct_rb/direct_rb.py,sha256=Cbx6B9q8Sqc_uPalX6fUEWJX6UEHCfWRof9u4brtx5A,48977
|
|
32
32
|
iqm/benchmarks/randomized_benchmarking/eplg/__init__.py,sha256=1MeGZTErElXJypQV2rQf7hwqLLvIp_JNVbwNhaP5vyI,696
|
|
33
|
-
iqm/benchmarks/randomized_benchmarking/eplg/eplg.py,sha256=
|
|
33
|
+
iqm/benchmarks/randomized_benchmarking/eplg/eplg.py,sha256=3A_gxzAs6mi3APKvqCwYDcNwRogIZNy5SDL33Cro89E,17036
|
|
34
34
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py,sha256=sq6MgN_hwlpkOj10vyCU4e6eKSX-oLcF2L9na6W2Gt4,681
|
|
35
35
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=TaR1YFWBhOgm1hmEQzuwLYpp0yl0Xpuo3jAT6YhiXpc,28471
|
|
36
36
|
iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=jRKbivWCZ3xdO1k0sx-ygC3s5DUkGSModd975PoAtcg,692
|
|
37
37
|
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=n_5gt9636ZDMsM9hC3Zm5qP2bQr2sy41zxGhOh0XMjI,32932
|
|
38
|
-
iqm_benchmarks-2.
|
|
38
|
+
iqm_benchmarks-2.45.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
|
|
39
39
|
mGST/LICENSE,sha256=TtHNq55cUcbglb7uhVudeBLUh_qPdUoAEvU0BBwFz-k,1098
|
|
40
40
|
mGST/README.md,sha256=v_5kw253csHF4-RfE-44KqFmBXIsSMRmOtN0AUPrRxE,5050
|
|
41
|
-
mGST/additional_fns.py,sha256=
|
|
42
|
-
mGST/algorithm.py,sha256=
|
|
41
|
+
mGST/additional_fns.py,sha256=MV0Pm5ap59IjhT_E3QhsZyM7lXOF1RZ9SD11zoaf43A,31781
|
|
42
|
+
mGST/algorithm.py,sha256=2xeIiPP1ae8IgBgrc7boTk-NyirUMJimcZTvKMbZqKY,27504
|
|
43
43
|
mGST/compatibility.py,sha256=00DsPnNfOtrQcDTvxBDs-0aMhmuXmOIIxl_Ohy-Emkg,8920
|
|
44
|
-
mGST/low_level_jit.py,sha256=
|
|
45
|
-
mGST/optimization.py,sha256=
|
|
46
|
-
mGST/qiskit_interface.py,sha256=
|
|
47
|
-
mGST/reporting/figure_gen.py,sha256=
|
|
48
|
-
mGST/reporting/reporting.py,sha256=
|
|
49
|
-
iqm_benchmarks-2.
|
|
50
|
-
iqm_benchmarks-2.
|
|
51
|
-
iqm_benchmarks-2.
|
|
52
|
-
iqm_benchmarks-2.
|
|
44
|
+
mGST/low_level_jit.py,sha256=Ih1MxcwU0GnSRu0TI9GaYxpB94CMuJ8Is1eTeOwsfA8,30772
|
|
45
|
+
mGST/optimization.py,sha256=x9tJ9wMQ5aONWpNpBMVtK0rwE6DRcOU33htNgrt0tx4,11015
|
|
46
|
+
mGST/qiskit_interface.py,sha256=uCdn-Q9CXI2f4FQSxGUy8GmmzQhr9NhCOFb2VPj0gTs,10061
|
|
47
|
+
mGST/reporting/figure_gen.py,sha256=xFPAHx1Trdqz7swn0kRqwc_jbRaNxhG9Nvx0jeitooo,25847
|
|
48
|
+
mGST/reporting/reporting.py,sha256=Wss1-zFsMEhzrrXKfP-RICau80ezjDIzcN555KhSehc,34160
|
|
49
|
+
iqm_benchmarks-2.45.dist-info/METADATA,sha256=a121AuXxTW_ziKGmIdEd-qjjBunWeV3iFEp60U4kT9o,10905
|
|
50
|
+
iqm_benchmarks-2.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
51
|
+
iqm_benchmarks-2.45.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
|
|
52
|
+
iqm_benchmarks-2.45.dist-info/RECORD,,
|
mGST/additional_fns.py
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
Utility functions used by mGST modules
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import contextlib
|
|
5
6
|
import os
|
|
6
7
|
import random
|
|
8
|
+
import sys
|
|
7
9
|
import warnings
|
|
8
10
|
|
|
9
11
|
import numpy as np
|
|
@@ -13,6 +15,18 @@ from scipy.linalg import expm, qr
|
|
|
13
15
|
from mGST.low_level_jit import Mp_norm_lower, MVE_lower, contract, local_basis
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
@contextlib.contextmanager
|
|
19
|
+
def suppress_stdout():
|
|
20
|
+
"""Context manager to temporarily suppress stdout output."""
|
|
21
|
+
with open(os.devnull, "w", encoding="utf-8") as devnull:
|
|
22
|
+
old_stdout = sys.stdout
|
|
23
|
+
sys.stdout = devnull
|
|
24
|
+
try:
|
|
25
|
+
yield
|
|
26
|
+
finally:
|
|
27
|
+
sys.stdout = old_stdout
|
|
28
|
+
|
|
29
|
+
|
|
16
30
|
def transp(dim1, dim2):
|
|
17
31
|
"""Superoperator of a map that performs the transpose operation
|
|
18
32
|
|
|
@@ -261,6 +275,10 @@ def random_gs(d, r, rK, n_povm):
|
|
|
261
275
|
The second axis enumerates Kraus operators for a gate specified by the first axis.
|
|
262
276
|
X: 3D numpy array
|
|
263
277
|
Array where random CPT superoperators are stacked along the first axis.
|
|
278
|
+
E: 2D numpy array
|
|
279
|
+
Randomly generated POVM elements stacked along the first axis.
|
|
280
|
+
rho: 1D numpy array
|
|
281
|
+
Random initial state vector
|
|
264
282
|
|
|
265
283
|
Notes:
|
|
266
284
|
The Kraus operators are generated from random unitaries, see function randKrausSet
|
|
@@ -327,7 +345,7 @@ def perturbed_target_init(X_target, rK):
|
|
|
327
345
|
"""
|
|
328
346
|
d, r, _ = X_target.shape
|
|
329
347
|
pdim = int(np.sqrt(r))
|
|
330
|
-
K_perturb = randKrausSet(d, r, rK, a=0.
|
|
348
|
+
K_perturb = randKrausSet(d, r, rK, a=0.05)
|
|
331
349
|
X_perturb = np.einsum("ijkl,ijnm -> iknlm", K_perturb, K_perturb.conj()).reshape((d, r, r))
|
|
332
350
|
X_init = np.einsum("ikl,ilm ->ikm", X_perturb, X_target)
|
|
333
351
|
K_init = Kraus_rep(X_init, d, pdim, rK)
|
|
@@ -627,8 +645,7 @@ def sampled_measurements(y, n):
|
|
|
627
645
|
y_new = np.maximum(np.minimum(y, 1), 0)
|
|
628
646
|
if np.sum(np.abs(y_new - y)) > 1e-6:
|
|
629
647
|
warnings.warn(
|
|
630
|
-
f"Warning: Probabilities capped to interval [0,1]"
|
|
631
|
-
f"l1-difference to input:%f" % np.sum(np.abs(y_new - y)),
|
|
648
|
+
f"Warning: Probabilities capped to interval [0,1], l1-difference to input: {np.sum(np.abs(y_new - y))}"
|
|
632
649
|
)
|
|
633
650
|
y = y_new
|
|
634
651
|
rng = np.random.default_rng()
|
|
@@ -723,24 +740,22 @@ def is_positive(X, E, rho):
|
|
|
723
740
|
eigvals = np.array([la.eigvals(X_choi[i]) for i in range(d)])
|
|
724
741
|
partial_traces = np.einsum("aiikl -> akl", X.reshape(d, pdim, pdim, pdim, pdim))
|
|
725
742
|
povm_eigvals = np.array([la.eigvals(E[i].reshape(pdim, pdim)) for i in range(n_povm)])
|
|
726
|
-
if np.any(np.imag(eigvals.reshape(-1) > 1e-10)):
|
|
727
|
-
print("Gates are not all hermitian.")
|
|
728
|
-
else:
|
|
729
|
-
for i in range(d):
|
|
730
|
-
print(f"Gate %i positive:" % i, np.all(eigvals[i, :] > -1e-10))
|
|
731
|
-
print(f"Gate %i trace preserving:" % i, la.norm(partial_traces[i] - np.eye(pdim)) < 1e-10)
|
|
732
|
-
print(f"Initial state positive:", np.all(la.eigvals(rho.reshape(pdim, pdim)) > -1e-10))
|
|
733
|
-
print(f"Initial state normalization:", np.trace(rho.reshape(pdim, pdim)))
|
|
734
|
-
print(
|
|
735
|
-
"fPOVM valid:",
|
|
736
|
-
np.all(
|
|
737
|
-
[
|
|
738
|
-
la.norm(np.sum(E, axis=0).reshape(pdim, pdim) - np.eye(pdim)) < 1e-10,
|
|
739
|
-
np.all(povm_eigvals.reshape(-1) > -1e-10),
|
|
740
|
-
]
|
|
741
|
-
),
|
|
742
|
-
)
|
|
743
743
|
|
|
744
|
+
# Check if gates are hermitian
|
|
745
|
+
assert np.all(np.imag(eigvals.reshape(-1)) < 1e-10)
|
|
746
|
+
|
|
747
|
+
# Check if gates are positive and trace preserving
|
|
748
|
+
for i in range(d):
|
|
749
|
+
assert np.all(eigvals[i, :] > -1e-10)
|
|
750
|
+
assert la.norm(partial_traces[i] - np.eye(pdim)) < 1e-10
|
|
751
|
+
|
|
752
|
+
# Initial state positivity and normalization
|
|
753
|
+
assert np.all(la.eigvals(rho.reshape(pdim, pdim)) > -1e-10)
|
|
754
|
+
assert np.abs(np.trace(rho.reshape(pdim, pdim))) - 1 < 1e-10
|
|
755
|
+
|
|
756
|
+
# POVM positivity and normalization
|
|
757
|
+
assert la.norm(np.sum(E, axis=0).reshape(pdim, pdim) - np.eye(pdim)) < 1e-6
|
|
758
|
+
assert np.all(povm_eigvals.reshape(-1) > -1e-10)
|
|
744
759
|
|
|
745
760
|
def tvd(X, E, rho, J, y_data):
|
|
746
761
|
"""Return the total variation distance between model probabilities for the circuits in J
|