iqm-benchmarks 2.32__py3-none-any.whl → 2.34__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.34.dist-info}/METADATA +5 -4
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.34.dist-info}/RECORD +17 -13
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.34.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.34.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.32.dist-info → iqm_benchmarks-2.34.dist-info}/top_level.txt +0 -0
iqm/benchmarks/utils.py
CHANGED
|
@@ -20,12 +20,14 @@ from functools import wraps
|
|
|
20
20
|
import itertools
|
|
21
21
|
from math import floor
|
|
22
22
|
import os
|
|
23
|
+
import random
|
|
23
24
|
from time import time
|
|
24
25
|
from typing import Any, Dict, Iterable, List, Literal, Optional, Sequence, Set, Tuple, Union, cast
|
|
25
26
|
import warnings
|
|
26
27
|
|
|
27
28
|
from more_itertools import chunked
|
|
28
29
|
from mthree.utils import final_measurement_mapping
|
|
30
|
+
import networkx as nx
|
|
29
31
|
import numpy as np
|
|
30
32
|
from numpy.random import Generator
|
|
31
33
|
from qiskit import ClassicalRegister, transpile
|
|
@@ -47,12 +49,12 @@ from iqm.qiskit_iqm.iqm_provider import IQMProvider
|
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
def timeit(f):
|
|
50
|
-
"""Calculates the amount of time a function takes to execute
|
|
52
|
+
"""Calculates the amount of time a function takes to execute.
|
|
51
53
|
|
|
52
54
|
Args:
|
|
53
|
-
f: The function to add the timing attribute to
|
|
55
|
+
f: The function to add the timing attribute to.
|
|
54
56
|
Returns:
|
|
55
|
-
The decorated function execution with logger statement of elapsed time in execution
|
|
57
|
+
The decorated function execution with logger statement of elapsed time in execution.
|
|
56
58
|
"""
|
|
57
59
|
|
|
58
60
|
@wraps(f)
|
|
@@ -168,7 +170,7 @@ def count_native_gates(
|
|
|
168
170
|
|
|
169
171
|
native_operations = backend.operation_names
|
|
170
172
|
|
|
171
|
-
if
|
|
173
|
+
if backend.has_resonators():
|
|
172
174
|
native_operations.append("move")
|
|
173
175
|
# Some backends may not include "barrier" in the operation_names attribute
|
|
174
176
|
if "barrier" not in native_operations:
|
|
@@ -281,6 +283,50 @@ def get_active_qubits(qc: QuantumCircuit) -> List[int]:
|
|
|
281
283
|
return list(active_qubits)
|
|
282
284
|
|
|
283
285
|
|
|
286
|
+
def extract_fidelities(cal_url: str) -> tuple[list[list[int]], list[float], str]:
|
|
287
|
+
"""Returns couplings and CZ-fidelities from calibration data URL
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
cal_url: str
|
|
291
|
+
The url under which the calibration data for the backend can be found
|
|
292
|
+
Returns:
|
|
293
|
+
list_couplings: List[List[int]]
|
|
294
|
+
A list of pairs, each of which is a qubit coupling for which the calibration
|
|
295
|
+
data contains a fidelity.
|
|
296
|
+
list_fids: List[float]
|
|
297
|
+
A list of CZ fidelities from the calibration url, ordered in the same way as list_couplings
|
|
298
|
+
topology: str
|
|
299
|
+
Name of the chip topology layout, currently either "star" or "crystal"
|
|
300
|
+
"""
|
|
301
|
+
headers = {"Accept": "application/json", "Authorization": "Bearer " + os.environ["IQM_TOKEN"]}
|
|
302
|
+
r = requests.get(cal_url, headers=headers, timeout=60)
|
|
303
|
+
calibration = r.json()
|
|
304
|
+
cal_keys = {
|
|
305
|
+
el2["key"]: (i, j) for i, el1 in enumerate(calibration["calibrations"]) for j, el2 in enumerate(el1["metrics"])
|
|
306
|
+
}
|
|
307
|
+
list_couplings = []
|
|
308
|
+
list_fids = []
|
|
309
|
+
if "double_move_gate_fidelity" in cal_keys.keys():
|
|
310
|
+
i, j = cal_keys["double_move_gate_fidelity"]
|
|
311
|
+
topology = "star"
|
|
312
|
+
else:
|
|
313
|
+
i, j = cal_keys["cz_gate_fidelity"]
|
|
314
|
+
topology = "crystal"
|
|
315
|
+
for item in calibration["calibrations"][i]["metrics"][j]["metrics"]:
|
|
316
|
+
qb1 = int(item["locus"][0][2:]) if item["locus"][0] != "COMP_R" else 0
|
|
317
|
+
qb2 = int(item["locus"][1][2:]) if item["locus"][1] != "COMP_R" else 0
|
|
318
|
+
if topology == "star":
|
|
319
|
+
list_couplings.append([qb1, qb2])
|
|
320
|
+
else:
|
|
321
|
+
list_couplings.append([qb1 - 1, qb2 - 1])
|
|
322
|
+
list_fids.append(float(item["value"]))
|
|
323
|
+
calibrated_qubits = set(np.array(list_couplings).reshape(-1))
|
|
324
|
+
qubit_mapping = {qubit: idx for idx, qubit in enumerate(calibrated_qubits)}
|
|
325
|
+
list_couplings = [[qubit_mapping[edge[0]], qubit_mapping[edge[1]]] for edge in list_couplings]
|
|
326
|
+
|
|
327
|
+
return list_couplings, list_fids, topology
|
|
328
|
+
|
|
329
|
+
|
|
284
330
|
# pylint: disable=too-many-branches
|
|
285
331
|
def get_iqm_backend(backend_label: str) -> IQMBackendBase:
|
|
286
332
|
"""Get the IQM backend object from a backend name (str).
|
|
@@ -320,8 +366,20 @@ def get_iqm_backend(backend_label: str) -> IQMBackendBase:
|
|
|
320
366
|
elif backend_label.lower() in ("iqmfakedeneb", "fakedeneb"):
|
|
321
367
|
backend_object = IQMFakeDeneb()
|
|
322
368
|
|
|
369
|
+
# ****** 16Q Resonator Star ******
|
|
370
|
+
# Sirius
|
|
371
|
+
elif backend_label.lower() == "sirius":
|
|
372
|
+
iqm_server_url = "https://cocos.resonance.meetiqm.com/sirius"
|
|
373
|
+
provider = IQMProvider(iqm_server_url)
|
|
374
|
+
backend_object = provider.get_backend()
|
|
375
|
+
# FakeSirius
|
|
376
|
+
# elif backend_label.lower() in ("iqmfakesirius", "fakesirius"):
|
|
377
|
+
# backend_object = IQMFakeSirius()
|
|
378
|
+
|
|
323
379
|
else:
|
|
324
|
-
raise ValueError(
|
|
380
|
+
raise ValueError(
|
|
381
|
+
f"Backend {backend_label} not supported. Try 'garnet', 'deneb', 'sirius', 'fakeadonis' or 'fakeapollo'."
|
|
382
|
+
)
|
|
325
383
|
|
|
326
384
|
return backend_object
|
|
327
385
|
|
|
@@ -517,12 +575,12 @@ def perform_backend_transpilation(
|
|
|
517
575
|
initial_layout=qubits if aux_qc is None else None,
|
|
518
576
|
routing_method=routing_method,
|
|
519
577
|
)
|
|
520
|
-
if
|
|
578
|
+
if backend.has_resonators():
|
|
521
579
|
transpiled = transpile_to_IQM(
|
|
522
580
|
qc, backend=backend, optimize_single_qubits=optimize_sqg, remove_final_rzs=drop_final_rz
|
|
523
581
|
)
|
|
524
582
|
if aux_qc is not None:
|
|
525
|
-
if
|
|
583
|
+
if backend.has_resonators():
|
|
526
584
|
if backend.num_qubits in qubits:
|
|
527
585
|
raise ValueError(
|
|
528
586
|
f"Label {backend.num_qubits} is reserved for Resonator - "
|
|
@@ -546,7 +604,7 @@ def perform_backend_transpilation(
|
|
|
546
604
|
if coupling_map == backend.coupling_map:
|
|
547
605
|
transpiled_qc_list = [transpile_and_optimize(qc) for qc in qc_list]
|
|
548
606
|
else: # The coupling map will be reduced if the physical layout is to be fixed
|
|
549
|
-
if
|
|
607
|
+
if backend.has_resonators():
|
|
550
608
|
aux_qc_list = [QuantumCircuit(backend.num_qubits + 1, q.num_clbits) for q in qc_list]
|
|
551
609
|
else:
|
|
552
610
|
aux_qc_list = [QuantumCircuit(backend.num_qubits, q.num_clbits) for q in qc_list]
|
|
@@ -555,6 +613,47 @@ def perform_backend_transpilation(
|
|
|
555
613
|
return transpiled_qc_list
|
|
556
614
|
|
|
557
615
|
|
|
616
|
+
def random_hamiltonian_path(G: nx.Graph, N: int) -> List[Tuple[int, int]]:
|
|
617
|
+
"""
|
|
618
|
+
Generates a random Hamiltonian path with N vertices from a given NetworkX graph.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
G (networkx.Graph): The input graph.
|
|
622
|
+
N (int): The desired number of vertices in the Hamiltonian path.
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
list: A list of edges (tuples of nodes) representing the Hamiltonian path, or an empty list if not possible.
|
|
626
|
+
"""
|
|
627
|
+
if N > len(G):
|
|
628
|
+
raise ValueError(
|
|
629
|
+
f"The number of vertices in the Hamiltonian path ({N}) cannot be greater than the number of nodes in the graph ({len(G)})"
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
nodes = list(G.nodes)
|
|
633
|
+
random.shuffle(nodes) # Shuffle nodes to introduce randomness
|
|
634
|
+
|
|
635
|
+
for start in nodes:
|
|
636
|
+
path = [start]
|
|
637
|
+
visited = set(path)
|
|
638
|
+
edges = []
|
|
639
|
+
|
|
640
|
+
while len(path) < N:
|
|
641
|
+
neighbors = [n for n in G.neighbors(path[-1]) if n not in visited]
|
|
642
|
+
|
|
643
|
+
if not neighbors:
|
|
644
|
+
break # Dead end, stop trying this path
|
|
645
|
+
|
|
646
|
+
next_node = random.choice(neighbors)
|
|
647
|
+
edges.append((int(path[-1]), int(next_node)))
|
|
648
|
+
path.append(next_node)
|
|
649
|
+
visited.add(next_node)
|
|
650
|
+
|
|
651
|
+
if len(path) == N:
|
|
652
|
+
return edges # Successfully found a Hamiltonian path of length N
|
|
653
|
+
|
|
654
|
+
return [] # No valid path found
|
|
655
|
+
|
|
656
|
+
|
|
558
657
|
def reduce_to_active_qubits(
|
|
559
658
|
circuit: QuantumCircuit, backend_topology: Optional[str] = None, backend_num_qubits=None
|
|
560
659
|
) -> QuantumCircuit:
|
|
@@ -725,6 +824,34 @@ def split_sequence_in_chunks(sequence_in: Sequence[Any], split_size: int) -> Lis
|
|
|
725
824
|
return [sequence_in[i : i + split_size] for i in range(0, len(sequence_in), split_size)]
|
|
726
825
|
|
|
727
826
|
|
|
827
|
+
def split_into_disjoint_pairs(pairs: Sequence[Tuple[int, int]]) -> List[List[Tuple[int, int]]]:
|
|
828
|
+
"""
|
|
829
|
+
Split a Sequence of pairs of integers into a List of a minimal number of Lists of disjoint pairs.
|
|
830
|
+
Example: input [(0,3), (2,3), (3,8), (8,13), (13,17), (17,18)] gives
|
|
831
|
+
output [[(0, 3), (8, 13), (17, 18)], [(2, 3), (13, 17)], [(3, 8)]].
|
|
832
|
+
|
|
833
|
+
# TODO: enable specifying a max split size of Lists of disjoint pairs. # pylint: disable=fixme
|
|
834
|
+
|
|
835
|
+
Args:
|
|
836
|
+
pairs (Sequence[Tuple[int, int]]): The input list of pairs of integers.
|
|
837
|
+
Returns:
|
|
838
|
+
List[List[Tuple[int, int]]]: A List of Lists of disjoint pairs.
|
|
839
|
+
"""
|
|
840
|
+
result: List[List[Tuple[int, int]]] = []
|
|
841
|
+
|
|
842
|
+
for pair in pairs:
|
|
843
|
+
added = False
|
|
844
|
+
for group in result:
|
|
845
|
+
if not any(elem in pair for p in group for elem in p):
|
|
846
|
+
group.append(pair)
|
|
847
|
+
added = True
|
|
848
|
+
break
|
|
849
|
+
if not added:
|
|
850
|
+
result.append([pair])
|
|
851
|
+
|
|
852
|
+
return result
|
|
853
|
+
|
|
854
|
+
|
|
728
855
|
@timeit
|
|
729
856
|
def sort_batches_by_final_layout(
|
|
730
857
|
transpiled_circuit_list: List[QuantumCircuit],
|
|
@@ -824,7 +951,9 @@ def submit_execute(
|
|
|
824
951
|
qcvv_logger.warning(
|
|
825
952
|
"Both max_gates_per_batch and max_circuits_per_batch are not None. Selecting the one giving the smallest batches."
|
|
826
953
|
)
|
|
827
|
-
batching_size = min(
|
|
954
|
+
batching_size = min(
|
|
955
|
+
cast(int, max_circuits_per_batch), max(1, floor(cast(int, max_gates_per_batch) / avg_gates_per_qc))
|
|
956
|
+
)
|
|
828
957
|
if batching_size == max_circuits_per_batch:
|
|
829
958
|
restriction = "max_circuits_per_batch"
|
|
830
959
|
else:
|
|
@@ -861,47 +990,3 @@ def xrvariable_to_counts(dataset: xr.Dataset, identifier: str, counts_range: int
|
|
|
861
990
|
dict(zip(list(dataset[f"{identifier}_state_{u}"].data), dataset[f"{identifier}_counts_{u}"].data))
|
|
862
991
|
for u in range(counts_range)
|
|
863
992
|
]
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
def extract_fidelities(cal_url: str) -> tuple[list[list[int]], list[float], str]:
|
|
867
|
-
"""Returns couplings and CZ-fidelities from calibration data URL
|
|
868
|
-
|
|
869
|
-
Args:
|
|
870
|
-
cal_url: str
|
|
871
|
-
The url under which the calibration data for the backend can be found
|
|
872
|
-
Returns:
|
|
873
|
-
list_couplings: List[List[int]]
|
|
874
|
-
A list of pairs, each of which is a qubit coupling for which the calibration
|
|
875
|
-
data contains a fidelity.
|
|
876
|
-
list_fids: List[float]
|
|
877
|
-
A list of CZ fidelities from the calibration url, ordered in the same way as list_couplings
|
|
878
|
-
topology: str
|
|
879
|
-
Name of the chip topology layout, currently either "star" or "crystal"
|
|
880
|
-
"""
|
|
881
|
-
headers = {"Accept": "application/json", "Authorization": "Bearer " + os.environ["IQM_TOKEN"]}
|
|
882
|
-
r = requests.get(cal_url, headers=headers, timeout=60)
|
|
883
|
-
calibration = r.json()
|
|
884
|
-
cal_keys = {
|
|
885
|
-
el2["key"]: (i, j) for i, el1 in enumerate(calibration["calibrations"]) for j, el2 in enumerate(el1["metrics"])
|
|
886
|
-
}
|
|
887
|
-
list_couplings = []
|
|
888
|
-
list_fids = []
|
|
889
|
-
if "double_move_gate_fidelity" in cal_keys.keys():
|
|
890
|
-
i, j = cal_keys["double_move_gate_fidelity"]
|
|
891
|
-
topology = "star"
|
|
892
|
-
else:
|
|
893
|
-
i, j = cal_keys["cz_gate_fidelity"]
|
|
894
|
-
topology = "crystal"
|
|
895
|
-
for item in calibration["calibrations"][i]["metrics"][j]["metrics"]:
|
|
896
|
-
qb1 = int(item["locus"][0][2:]) if "COMP" not in item["locus"][0] else 0
|
|
897
|
-
qb2 = int(item["locus"][1][2:]) if "COMP" not in item["locus"][1] else 0
|
|
898
|
-
list_couplings.append([qb1 - 1, qb2 - 1])
|
|
899
|
-
list_fids.append(float(item["value"]))
|
|
900
|
-
calibrated_qubits = set(np.array(list_couplings).reshape(-1))
|
|
901
|
-
qubit_mapping = {}
|
|
902
|
-
if topology == "star":
|
|
903
|
-
qubit_mapping.update({-1: len(calibrated_qubits)}) # Place resonator qubit as last qubit
|
|
904
|
-
qubit_mapping.update({qubit: idx for idx, qubit in enumerate(calibrated_qubits)})
|
|
905
|
-
list_couplings = [[qubit_mapping[edge[0]], qubit_mapping[edge[1]]] for edge in list_couplings]
|
|
906
|
-
|
|
907
|
-
return list_couplings, list_fids, topology
|
iqm/benchmarks/utils_plots.py
CHANGED
|
@@ -16,15 +16,20 @@
|
|
|
16
16
|
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
21
|
|
|
22
|
+
from matplotlib.figure import Figure
|
|
21
23
|
import matplotlib.pyplot as plt
|
|
22
24
|
import networkx as nx
|
|
23
25
|
import numpy as np
|
|
24
26
|
from qiskit.transpiler import CouplingMap
|
|
27
|
+
import requests
|
|
25
28
|
from rustworkx import PyGraph, spring_layout, visualization # pylint: disable=no-name-in-module
|
|
26
29
|
|
|
27
|
-
from iqm.benchmarks.
|
|
30
|
+
from iqm.benchmarks.logging_config import qcvv_logger
|
|
31
|
+
from iqm.benchmarks.utils import extract_fidelities, get_iqm_backend, random_hamiltonian_path
|
|
32
|
+
from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
@dataclass
|
|
@@ -73,7 +78,14 @@ class GraphPositions:
|
|
|
73
78
|
6: (3.0, 3.0),
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
predefined_stations = {
|
|
81
|
+
predefined_stations = {
|
|
82
|
+
"garnet": garnet_positions,
|
|
83
|
+
"fakeapollo": garnet_positions,
|
|
84
|
+
"iqmfakeapollo": garnet_positions,
|
|
85
|
+
"deneb": deneb_positions,
|
|
86
|
+
"fakedeneb": deneb_positions,
|
|
87
|
+
"iqmfakedeneb": deneb_positions,
|
|
88
|
+
}
|
|
77
89
|
|
|
78
90
|
@staticmethod
|
|
79
91
|
def create_positions(
|
|
@@ -117,6 +129,179 @@ class GraphPositions:
|
|
|
117
129
|
return pos
|
|
118
130
|
|
|
119
131
|
|
|
132
|
+
def draw_graph_edges(
|
|
133
|
+
backend_coupling_map: CouplingMap,
|
|
134
|
+
backend_num_qubits: int,
|
|
135
|
+
edge_list: Sequence[Tuple[int, int]],
|
|
136
|
+
timestamp: str,
|
|
137
|
+
disjoint_layers: Optional[Sequence[Sequence[Tuple[int, int]]]] = None,
|
|
138
|
+
station: Optional[str] = None,
|
|
139
|
+
qubit_names: Optional[Dict[int, str]] = None,
|
|
140
|
+
is_eplg: Optional[bool] = False,
|
|
141
|
+
) -> Tuple[str, Figure]:
|
|
142
|
+
"""Draw given edges on a graph within the given backend.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
backend_coupling_map (CouplingMap): The coupling map to draw the graph from.
|
|
146
|
+
backend_num_qubits (int): The number of qubits of the respectve backend.
|
|
147
|
+
edge_list (Sequence[Tuple[int, int]]): The edge list of the linear chain.
|
|
148
|
+
timestamp (str): The timestamp to include in the figure name.
|
|
149
|
+
disjoint_layers (Optional[Sequence[Sequence[Tuple[int, int]]]): Sequences of edges defining disjoint layers to draw.
|
|
150
|
+
* Default is None.
|
|
151
|
+
station (Optional[str]): The name of the station.
|
|
152
|
+
* Default is None.
|
|
153
|
+
qubit_names (Optional[Dict[int, str]]): A dictionary mapping qubit indices to their names.
|
|
154
|
+
* Default is None.
|
|
155
|
+
is_eplg (Optional[bool]): A flag indicating if the graph refers to an EPLG experiment.
|
|
156
|
+
* Default is False.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Tuple[str, Figure]: The figure name and the figure object.
|
|
160
|
+
"""
|
|
161
|
+
disjoint = "_disjoint" if disjoint_layers is not None else ""
|
|
162
|
+
fig_name_station = f"_{station.lower()}" if station is not None else ""
|
|
163
|
+
fig_name = f"edges_graph{disjoint}{fig_name_station}_{timestamp}"
|
|
164
|
+
|
|
165
|
+
fig = plt.figure()
|
|
166
|
+
ax = plt.axes()
|
|
167
|
+
|
|
168
|
+
if station is not None:
|
|
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)
|
|
185
|
+
|
|
186
|
+
label_station = station if station is not None else f"{backend_num_qubits}-qubit IQM Backend"
|
|
187
|
+
if disjoint_layers is None:
|
|
188
|
+
nx.draw_networkx(
|
|
189
|
+
rx_to_nx_graph(backend_coupling_map),
|
|
190
|
+
pos=qubit_positions,
|
|
191
|
+
edgelist=edge_list,
|
|
192
|
+
width=4.0,
|
|
193
|
+
edge_color="k",
|
|
194
|
+
node_color="k",
|
|
195
|
+
font_color="w",
|
|
196
|
+
ax=ax,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
plt.title(f"Selected edges in {label_station}\n" f"\n{timestamp}")
|
|
200
|
+
|
|
201
|
+
else:
|
|
202
|
+
num_disjoint_layers = len(disjoint_layers)
|
|
203
|
+
colors = plt.colormaps["rainbow"](np.linspace(0, 1, num_disjoint_layers))
|
|
204
|
+
all_edge_colors = [[colors[i]] * len(l) for i, l in enumerate(disjoint_layers)] # Flatten below
|
|
205
|
+
nx.draw_networkx(
|
|
206
|
+
rx_to_nx_graph(backend_coupling_map),
|
|
207
|
+
pos=qubit_positions,
|
|
208
|
+
labels=(
|
|
209
|
+
{x: qubit_names[x] for x in range(backend_num_qubits)}
|
|
210
|
+
if qubit_names
|
|
211
|
+
else list(range(backend_num_qubits))
|
|
212
|
+
),
|
|
213
|
+
font_size=6.5 if qubit_names else 10,
|
|
214
|
+
edgelist=[x for y in disjoint_layers for x in y],
|
|
215
|
+
width=4.0,
|
|
216
|
+
edge_color=[x for y in all_edge_colors for x in y],
|
|
217
|
+
node_color="k",
|
|
218
|
+
font_color="w",
|
|
219
|
+
ax=ax,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
is_eplg_string = " for EPLG experiment" if is_eplg else ""
|
|
223
|
+
plt.title(
|
|
224
|
+
f"Selected edges in {label_station.capitalize()}{is_eplg_string}\n"
|
|
225
|
+
f"{len(disjoint_layers)} groups of disjoint layers"
|
|
226
|
+
f"\n{timestamp}"
|
|
227
|
+
)
|
|
228
|
+
ax.set_aspect(0.925)
|
|
229
|
+
plt.close()
|
|
230
|
+
|
|
231
|
+
return fig_name, fig
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def evaluate_hamiltonian_paths(
|
|
235
|
+
N: int,
|
|
236
|
+
path_samples: int,
|
|
237
|
+
backend_arg: str | IQMBackendBase,
|
|
238
|
+
url: str,
|
|
239
|
+
max_tries: int = 10,
|
|
240
|
+
) -> Dict[int, List[Tuple[int, int]]]:
|
|
241
|
+
"""Evaluates Hamiltonian paths according to the product of 2Q gate fidelities on the corresponding edges of the backend graph.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
N (int): the number of vertices in the Hamiltonian paths to evaluate.
|
|
245
|
+
path_samples (int): the number of Hamiltonian paths to evaluate.
|
|
246
|
+
backend_arg (str | IQMBackendBase): the backend to evaluate the Hamiltonian paths on with respect to fidelity.
|
|
247
|
+
url (str): the URL address for the backend to retrieve calibration data from.
|
|
248
|
+
max_tries (int): the maximum number of tries to generate a Hamiltonian path.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Dict[int, List[Tuple[int, int]]]: A dictionary with keys being fidelity products and values being the respective Hamiltonian paths.
|
|
252
|
+
"""
|
|
253
|
+
if isinstance(backend_arg, str):
|
|
254
|
+
backend = get_iqm_backend(backend_arg)
|
|
255
|
+
else:
|
|
256
|
+
backend = backend_arg
|
|
257
|
+
|
|
258
|
+
backend_nx_graph = rx_to_nx_graph(backend.coupling_map)
|
|
259
|
+
|
|
260
|
+
all_paths = []
|
|
261
|
+
sample_counter = 0
|
|
262
|
+
tries = 0
|
|
263
|
+
while sample_counter < path_samples and tries <= max_tries:
|
|
264
|
+
h_path = random_hamiltonian_path(backend_nx_graph, N)
|
|
265
|
+
if not h_path:
|
|
266
|
+
qcvv_logger.debug(f"Failed to generate a Hamiltonian path with {N} vertices - retrying...")
|
|
267
|
+
tries += 1
|
|
268
|
+
if tries == max_tries:
|
|
269
|
+
raise RecursionError(
|
|
270
|
+
f"Max tries to generate a Hamiltonian path with {N} vertices reached - Try with less vertices!\n"
|
|
271
|
+
f"For EPLG, you may also manually specify qubit pairs."
|
|
272
|
+
)
|
|
273
|
+
continue
|
|
274
|
+
all_paths.append(h_path)
|
|
275
|
+
tries = 0
|
|
276
|
+
sample_counter += 1
|
|
277
|
+
|
|
278
|
+
# Get scores for all paths
|
|
279
|
+
# Retrieve fidelity data
|
|
280
|
+
two_qubit_fidelity = {}
|
|
281
|
+
|
|
282
|
+
headers = {"Accept": "application/json", "Authorization": "Bearer " + os.environ["IQM_TOKEN"]}
|
|
283
|
+
r = requests.get(url, headers=headers, timeout=60)
|
|
284
|
+
calibration = r.json()
|
|
285
|
+
|
|
286
|
+
for iq in calibration["calibrations"][0]["metrics"][0]["metrics"]:
|
|
287
|
+
temp = list(iq.values())
|
|
288
|
+
two_qubit_fidelity[str(temp[0])] = temp[1]
|
|
289
|
+
two_qubit_fidelity[str([temp[0][1], temp[0][0]])] = temp[1]
|
|
290
|
+
|
|
291
|
+
# Rate all the paths
|
|
292
|
+
path_costs = {} # keys are costs, values are edge paths
|
|
293
|
+
for h_path in all_paths:
|
|
294
|
+
total_cost = 1
|
|
295
|
+
for edge in h_path:
|
|
296
|
+
if len(edge) == 2:
|
|
297
|
+
total_cost *= two_qubit_fidelity[
|
|
298
|
+
str([backend.index_to_qubit_name(edge[0]), backend.index_to_qubit_name(edge[1])])
|
|
299
|
+
]
|
|
300
|
+
path_costs[total_cost] = h_path
|
|
301
|
+
|
|
302
|
+
return path_costs
|
|
303
|
+
|
|
304
|
+
|
|
120
305
|
def plot_layout_fidelity_graph(
|
|
121
306
|
cal_url: str, qubit_layouts: Optional[list[list[int]]] = None, station: Optional[str] = None
|
|
122
307
|
):
|
|
@@ -208,6 +393,7 @@ def rx_to_nx_graph(backend_coupling_map: CouplingMap) -> nx.Graph:
|
|
|
208
393
|
networkx.Graph: The Networkx Graph corresponding to the backend graph.
|
|
209
394
|
|
|
210
395
|
"""
|
|
396
|
+
|
|
211
397
|
# Generate a Networkx graph
|
|
212
398
|
graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
|
|
213
399
|
backend_egdes, backend_nodes = (list(graph_backend.edge_list()), list(graph_backend.node_indices()))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iqm-benchmarks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.34
|
|
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
|
|
@@ -18,9 +18,9 @@ Requires-Dist: mthree<2.7,>=2.6
|
|
|
18
18
|
Requires-Dist: networkx<4.0,>=3.3
|
|
19
19
|
Requires-Dist: rustworkx>=0.16.0
|
|
20
20
|
Requires-Dist: numpy<2.0,>=1.25.2
|
|
21
|
-
Requires-Dist: qiskit<
|
|
22
|
-
Requires-Dist: qiskit
|
|
23
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: qiskit<1.3,>=1.2.4
|
|
22
|
+
Requires-Dist: iqm-client[qiskit]<24.0,>=23.8
|
|
23
|
+
Requires-Dist: requests<3.0,>=2.32.3
|
|
24
24
|
Requires-Dist: scikit-optimize<0.11.0,>=0.10.2
|
|
25
25
|
Requires-Dist: tabulate<1.0.0,>=0.9.0
|
|
26
26
|
Requires-Dist: uncertainties<3.3.0,>=3.2.2
|
|
@@ -64,6 +64,7 @@ Below is a list of the benchmarks currently available in the suite:
|
|
|
64
64
|
- Interleaved Randomized Benchmarking [[Phys. Rev. Lett. 109, 080505](https://doi.org/10.1103/PhysRevLett.109.080505) (2012)]
|
|
65
65
|
- Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)]
|
|
66
66
|
- Mirror Randomized Benchmarking [[Phys. Rev. Lett. 129, 150502](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.129.150502) (2022)]
|
|
67
|
+
- Error Per Layered Gate [[arXiv:2311.05933 [quant-ph]](https://arxiv.org/abs/2311.05933) (2023)]
|
|
67
68
|
* Holistic:
|
|
68
69
|
- Quantum Volume [[Phys. Rev. A 100, 032328](https://doi.org/10.1103/PhysRevA.100.032328) (2019)]
|
|
69
70
|
- CLOPS [[arXiv:2110.14108 [quant-ph]](https://arxiv.org/abs/2110.14108) (2021)]
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
iqm/benchmarks/__init__.py,sha256
|
|
1
|
+
iqm/benchmarks/__init__.py,sha256=-drZ_whue067Tu4I9RxBCqrrzu38Tm5Kqf9jHTftUPk,3070
|
|
2
2
|
iqm/benchmarks/benchmark.py,sha256=3E7g7RQjCIGIpSI1gOSrI3V9SAVs-XEOMrPgToK_7vw,4972
|
|
3
3
|
iqm/benchmarks/benchmark_definition.py,sha256=e4xe0wlWKZqj48_6-zTglMaMeoiA9aGkHrrSgoCfPkM,11271
|
|
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=HywFLNDq713HYebtQfKpzP_cXXD9DbyPGdxArsukQbw,41233
|
|
8
|
+
iqm/benchmarks/utils_plots.py,sha256=Q4h7gcKXf8Eizm13P0yL2I_P-QobHVFr9JCV83wrUi8,14942
|
|
9
9
|
iqm/benchmarks/utils_shadows.py,sha256=e77PV_uaAO5m_woox9lAzompKAvFeDJ-0AKJrNJ7NFg,9728
|
|
10
10
|
iqm/benchmarks/compressive_gst/__init__.py,sha256=LneifgYXtcwo2jcXo7GdUEHL6_peipukShhkrdaTRCA,929
|
|
11
11
|
iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=2kiRttog4jR-vtMHu847GTFe5qL_i_uYr_4WMGqt9Ww,25653
|
|
12
|
-
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=
|
|
12
|
+
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=g0kEovWbetoDRvX7JFrS9oOoNrqBxaFmprujJi7qQbU,36297
|
|
13
13
|
iqm/benchmarks/entanglement/__init__.py,sha256=sHVVToRWRCz0LSntk1rQaoSNNeyZLPoiTjUKWZWrk1E,778
|
|
14
14
|
iqm/benchmarks/entanglement/ghz.py,sha256=RGA6ynJFsfaCJv0nKccsiIzPk2G-iHHvIeW8LVu30HY,41249
|
|
15
|
-
iqm/benchmarks/entanglement/graph_states.py,sha256=
|
|
15
|
+
iqm/benchmarks/entanglement/graph_states.py,sha256=qv6nAgbvm1toSgNGLjBA8DpF9fN7UUlMpPeR_gixMcI,62561
|
|
16
16
|
iqm/benchmarks/optimization/__init__.py,sha256=_ajW_OibYLCtzU5AUv5c2zuuVYn8ZNeZUcUUSIGt51M,747
|
|
17
17
|
iqm/benchmarks/optimization/qscore.py,sha256=KOw8fjXeMwCjYvKukpX7IiAqRQrVTkrnIgKjNCPVWdw,38130
|
|
18
18
|
iqm/benchmarks/quantum_volume/__init__.py,sha256=i-Q4SpDWELBw7frXnxm1j4wJRcxbIyrS5uEK_v06YHo,951
|
|
@@ -22,14 +22,18 @@ iqm/benchmarks/randomized_benchmarking/__init__.py,sha256=IkKo-7zUChxZZd3my_csQC
|
|
|
22
22
|
iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl,sha256=yrmSJqhv7Lb1yqiqU9-2baqTljJPNmTUPQR-AH6GGfc,7800
|
|
23
23
|
iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl,sha256=mJQLubWPOb-DbmFi4oKYJqAMW_Yyo3eJjRjLGl9Sqmo,10282247
|
|
24
24
|
iqm/benchmarks/randomized_benchmarking/multi_lmfit.py,sha256=Se1ygR4mXn_2_P82Ch31KBnCmY-g_A9NKzE9Ir8nEvw,3247
|
|
25
|
-
iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py,sha256=
|
|
25
|
+
iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py,sha256=kX4ODF7MEB5olYKjpnUVon0hJ0irTOxz-z1exCa7mqc,54286
|
|
26
26
|
iqm/benchmarks/randomized_benchmarking/clifford_rb/__init__.py,sha256=bTDA156LAl7OLGcMec--1nzDrV1XpPRVq3CquTmucgE,677
|
|
27
27
|
iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py,sha256=IGBrq_a9eaVPknkBLKHKS4BOcumHn6TZdasUNKTZjGI,18685
|
|
28
|
+
iqm/benchmarks/randomized_benchmarking/direct_rb/__init__.py,sha256=lCIIeWMFZHnMUUEUTjUBvrhhUur6uBTHIVkxFBSfHC4,681
|
|
29
|
+
iqm/benchmarks/randomized_benchmarking/direct_rb/direct_rb.py,sha256=Cbx6B9q8Sqc_uPalX6fUEWJX6UEHCfWRof9u4brtx5A,48977
|
|
30
|
+
iqm/benchmarks/randomized_benchmarking/eplg/__init__.py,sha256=1MeGZTErElXJypQV2rQf7hwqLLvIp_JNVbwNhaP5vyI,696
|
|
31
|
+
iqm/benchmarks/randomized_benchmarking/eplg/eplg.py,sha256=VifpRgw8AHHu1NG9N86lcF0lFhFtauQUaclk5mc__EY,17217
|
|
28
32
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py,sha256=sq6MgN_hwlpkOj10vyCU4e6eKSX-oLcF2L9na6W2Gt4,681
|
|
29
33
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=TaR1YFWBhOgm1hmEQzuwLYpp0yl0Xpuo3jAT6YhiXpc,28471
|
|
30
|
-
iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=
|
|
31
|
-
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=
|
|
32
|
-
iqm_benchmarks-2.
|
|
34
|
+
iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=jRKbivWCZ3xdO1k0sx-ygC3s5DUkGSModd975PoAtcg,692
|
|
35
|
+
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=n_5gt9636ZDMsM9hC3Zm5qP2bQr2sy41zxGhOh0XMjI,32932
|
|
36
|
+
iqm_benchmarks-2.34.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
|
|
33
37
|
mGST/LICENSE,sha256=TtHNq55cUcbglb7uhVudeBLUh_qPdUoAEvU0BBwFz-k,1098
|
|
34
38
|
mGST/README.md,sha256=v_5kw253csHF4-RfE-44KqFmBXIsSMRmOtN0AUPrRxE,5050
|
|
35
39
|
mGST/additional_fns.py,sha256=_SEJ10FRNM7_CroysT8hCLZTfpm6ZhEIDCY5zPTnhjo,31390
|
|
@@ -40,7 +44,7 @@ mGST/optimization.py,sha256=YHwkzIkYvsZOPjclR-BCQWh24jeqjuXp0BB0WX5Lwow,10559
|
|
|
40
44
|
mGST/qiskit_interface.py,sha256=ajx6Zn5FnrX_T7tMP8xnBLyG4c2ddFRm0Fu2_3r1t30,10118
|
|
41
45
|
mGST/reporting/figure_gen.py,sha256=6Xd8vwfy09hLY1YbJY6TRevuMsQSU4MsWqemly3ZO0I,12970
|
|
42
46
|
mGST/reporting/reporting.py,sha256=B8NWfpZrrSmyH7lwZxd0EbZMYLsAGK1YsHRB4D5qXH4,26002
|
|
43
|
-
iqm_benchmarks-2.
|
|
44
|
-
iqm_benchmarks-2.
|
|
45
|
-
iqm_benchmarks-2.
|
|
46
|
-
iqm_benchmarks-2.
|
|
47
|
+
iqm_benchmarks-2.34.dist-info/METADATA,sha256=fb_FEq_EPL2KyocKMuAu09q21xLh6Dfbc0ZrlDzJU6Q,10823
|
|
48
|
+
iqm_benchmarks-2.34.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
49
|
+
iqm_benchmarks-2.34.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
|
|
50
|
+
iqm_benchmarks-2.34.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|