iqm-benchmarks 1.6__py3-none-any.whl → 1.8__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/compressive_gst/compressive_gst.py +147 -720
- iqm/benchmarks/compressive_gst/gst_analysis.py +851 -0
- iqm/benchmarks/entanglement/ghz.py +181 -119
- iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl +0 -0
- iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl +0 -0
- iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py +6 -4
- iqm/benchmarks/readout_mitigation.py +3 -8
- iqm/benchmarks/utils.py +0 -31
- {iqm_benchmarks-1.6.dist-info → iqm_benchmarks-1.8.dist-info}/METADATA +19 -13
- {iqm_benchmarks-1.6.dist-info → iqm_benchmarks-1.8.dist-info}/RECORD +13 -12
- {iqm_benchmarks-1.6.dist-info → iqm_benchmarks-1.8.dist-info}/LICENSE +0 -0
- {iqm_benchmarks-1.6.dist-info → iqm_benchmarks-1.8.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-1.6.dist-info → iqm_benchmarks-1.8.dist-info}/top_level.txt +0 -0
|
@@ -19,14 +19,18 @@ GHZ state benchmark
|
|
|
19
19
|
from io import BytesIO
|
|
20
20
|
from itertools import chain
|
|
21
21
|
import json
|
|
22
|
-
from time import strftime
|
|
23
|
-
from typing import Dict, List, Optional, Tuple, Type, cast
|
|
22
|
+
from time import strftime
|
|
23
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, cast
|
|
24
24
|
|
|
25
|
+
from matplotlib.figure import Figure
|
|
26
|
+
import matplotlib.pyplot as plt
|
|
27
|
+
import networkx
|
|
25
28
|
from networkx import Graph, all_pairs_shortest_path, is_connected, minimum_spanning_tree
|
|
26
29
|
import numpy as np
|
|
27
30
|
import pycurl
|
|
28
31
|
from qiskit import QuantumCircuit, transpile
|
|
29
32
|
from qiskit.quantum_info import random_clifford
|
|
33
|
+
from qiskit.transpiler import CouplingMap
|
|
30
34
|
from qiskit_aer import Aer
|
|
31
35
|
from scipy.spatial.distance import hamming
|
|
32
36
|
import xarray as xr
|
|
@@ -45,7 +49,9 @@ from iqm.benchmarks.readout_mitigation import apply_readout_error_mitigation
|
|
|
45
49
|
from iqm.benchmarks.utils import (
|
|
46
50
|
perform_backend_transpilation,
|
|
47
51
|
reduce_to_active_qubits,
|
|
52
|
+
retrieve_all_counts,
|
|
48
53
|
set_coupling_map,
|
|
54
|
+
submit_execute,
|
|
49
55
|
timeit,
|
|
50
56
|
xrvariable_to_counts,
|
|
51
57
|
)
|
|
@@ -68,16 +74,17 @@ def append_rms(
|
|
|
68
74
|
Returns:
|
|
69
75
|
List[QuantumCircuit] of the original circuit with 1Q Clifford gates appended to it
|
|
70
76
|
"""
|
|
71
|
-
rm_circuits = []
|
|
77
|
+
rm_circuits: list[QuantumCircuit] = []
|
|
72
78
|
for _ in range(num_rms):
|
|
73
79
|
rm_circ = circuit.copy()
|
|
74
80
|
# It shouldn't matter if measurement bits get scrambled
|
|
75
81
|
rm_circ.remove_final_measurements()
|
|
76
82
|
|
|
77
83
|
active_qubits = set()
|
|
78
|
-
|
|
84
|
+
data = rm_circ.data
|
|
85
|
+
for instruction in data:
|
|
79
86
|
for qubit in instruction[1]:
|
|
80
|
-
active_qubits.add(qubit
|
|
87
|
+
active_qubits.add(rm_circ.find_bit(qubit)[0])
|
|
81
88
|
|
|
82
89
|
for q in active_qubits:
|
|
83
90
|
if backend is not None:
|
|
@@ -97,7 +104,7 @@ def append_rms(
|
|
|
97
104
|
|
|
98
105
|
def fidelity_ghz_randomized_measurements(
|
|
99
106
|
dataset: xr.Dataset, qubit_layout, ideal_probabilities: List[Dict[str, int]], num_qubits: int
|
|
100
|
-
) ->
|
|
107
|
+
) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
101
108
|
"""
|
|
102
109
|
Estimates GHZ state fidelity through cross-correlations of RMs.
|
|
103
110
|
Implementation of Eq. (34) in https://arxiv.org/abs/1812.02624
|
|
@@ -108,21 +115,24 @@ def fidelity_ghz_randomized_measurements(
|
|
|
108
115
|
ideal_probabilities (List[Dict[str, int]]):
|
|
109
116
|
num_qubits (int):
|
|
110
117
|
Returns:
|
|
111
|
-
|
|
118
|
+
values: dict[str, Any]
|
|
119
|
+
The fidelities
|
|
120
|
+
uncertainties: dict[str, Any]
|
|
121
|
+
The uncertainties for the fidelities
|
|
112
122
|
"""
|
|
113
|
-
|
|
123
|
+
idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
114
124
|
# List for each RM contribution to the fidelity
|
|
115
125
|
fid_rm = []
|
|
116
126
|
|
|
117
127
|
# Loop through RMs and add each contribution
|
|
118
|
-
num_rms = len(dataset.attrs["transpiled_circuits"][f"{
|
|
128
|
+
num_rms = len(dataset.attrs["transpiled_circuits"][f"{idx}"])
|
|
119
129
|
for u in range(num_rms):
|
|
120
130
|
# Probability estimates for noisy measurements
|
|
121
131
|
probabilities_sample = {}
|
|
122
|
-
c_keys = dataset[f"{
|
|
123
|
-
num_shots_noisy = sum(dataset[f"{
|
|
132
|
+
c_keys = dataset[f"{idx}_state_{u}"].data # measurements[u].keys()
|
|
133
|
+
num_shots_noisy = sum(dataset[f"{idx}_counts_{u}"].data)
|
|
124
134
|
for k, key in enumerate(c_keys):
|
|
125
|
-
probabilities_sample[key] = dataset[f"{
|
|
135
|
+
probabilities_sample[key] = dataset[f"{idx}_counts_{u}"].data[k] / num_shots_noisy
|
|
126
136
|
# Keys for corresponding ideal probabilities
|
|
127
137
|
c_id_keys = ideal_probabilities[u].keys()
|
|
128
138
|
|
|
@@ -132,17 +142,18 @@ def fidelity_ghz_randomized_measurements(
|
|
|
132
142
|
exponent = hamming(list(sa), list(sb)) * num_qubits
|
|
133
143
|
p_sum.append(np.power(-2, -exponent) * probabilities_sample[sa] * ideal_probabilities[u][sb])
|
|
134
144
|
fid_rm.append((2**num_qubits) * sum(p_sum))
|
|
135
|
-
|
|
145
|
+
values = {"fidelity": np.mean(fid_rm)}
|
|
146
|
+
uncertainties = {"fidelity": np.std(fid_rm) / np.sqrt(num_rms)}
|
|
136
147
|
|
|
137
148
|
if dataset.attrs["rem"]:
|
|
138
149
|
fid_rm_rem = []
|
|
139
150
|
for u in range(num_rms):
|
|
140
151
|
# Probability estimates for noisy measurements
|
|
141
152
|
probabilities_sample = {}
|
|
142
|
-
c_keys = dataset[f"{
|
|
143
|
-
num_shots_noisy = sum(dataset[f"{
|
|
153
|
+
c_keys = dataset[f"{idx}_rem_state_{u}"].data # measurements[u].keys()
|
|
154
|
+
num_shots_noisy = sum(dataset[f"{idx}_rem_counts_{u}"].data)
|
|
144
155
|
for k, key in enumerate(c_keys):
|
|
145
|
-
probabilities_sample[key] = dataset[f"{
|
|
156
|
+
probabilities_sample[key] = dataset[f"{idx}_rem_counts_{u}"].data[k] / num_shots_noisy
|
|
146
157
|
# Keys for corresponding ideal probabilities
|
|
147
158
|
c_id_keys = ideal_probabilities[u].keys()
|
|
148
159
|
|
|
@@ -152,11 +163,12 @@ def fidelity_ghz_randomized_measurements(
|
|
|
152
163
|
exponent = hamming(list(sa), list(sb)) * num_qubits
|
|
153
164
|
p_sum.append(np.power(-2, -exponent) * probabilities_sample[sa] * ideal_probabilities[u][sb])
|
|
154
165
|
fid_rm_rem.append((2**num_qubits) * sum(p_sum))
|
|
155
|
-
|
|
156
|
-
|
|
166
|
+
values = values | {"fidelity_rem": np.mean(fid_rm_rem)}
|
|
167
|
+
uncertainties = uncertainties | {"fidelity_rem": np.std(fid_rm_rem) / np.sqrt(num_rms)}
|
|
168
|
+
return values, uncertainties
|
|
157
169
|
|
|
158
170
|
|
|
159
|
-
def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) ->
|
|
171
|
+
def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> list[Any]:
|
|
160
172
|
"""
|
|
161
173
|
Estimates the GHZ state fidelity based on the multiple quantum coherences method based on [Mooney, 2021]
|
|
162
174
|
|
|
@@ -167,12 +179,13 @@ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> Lis
|
|
|
167
179
|
The subset of system-qubits used in the protocol
|
|
168
180
|
|
|
169
181
|
Returns:
|
|
170
|
-
|
|
182
|
+
dict[str, dict[str, Any]]: The ghz fidelity or, if rem=True, fidelity and readout error mitigated fidelity
|
|
171
183
|
"""
|
|
172
184
|
|
|
173
185
|
num_qubits = len(qubit_layout)
|
|
174
186
|
phases = [np.pi * i / (num_qubits + 1) for i in range(2 * num_qubits + 2)]
|
|
175
|
-
|
|
187
|
+
idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
188
|
+
transpiled_circuits = dataset.attrs["transpiled_circuits"][idx]
|
|
176
189
|
num_shots = dataset.attrs["shots"]
|
|
177
190
|
num_circuits = len(transpiled_circuits)
|
|
178
191
|
|
|
@@ -180,16 +193,7 @@ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> Lis
|
|
|
180
193
|
complex_coefficients = np.exp(1j * num_qubits * np.array(phases))
|
|
181
194
|
|
|
182
195
|
# Loading the counts from the dataset
|
|
183
|
-
counts = xrvariable_to_counts(dataset,
|
|
184
|
-
# for u in range(num_circuits):
|
|
185
|
-
# counts.append(
|
|
186
|
-
# dict(
|
|
187
|
-
# zip(
|
|
188
|
-
# list(dataset[f"{str(qubit_layout)}_state_{u}"].data),
|
|
189
|
-
# dataset[f"{str(qubit_layout)}_counts_{u}"].data,
|
|
190
|
-
# )
|
|
191
|
-
# )
|
|
192
|
-
# )
|
|
196
|
+
counts = xrvariable_to_counts(dataset, f"{idx}", num_circuits)
|
|
193
197
|
all_zero_probability_list = [] # An ordered list for storing the probabilities of returning to the |00..0> state
|
|
194
198
|
for count in counts[1:]:
|
|
195
199
|
if "0" * num_qubits in count.keys():
|
|
@@ -211,16 +215,7 @@ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> Lis
|
|
|
211
215
|
|
|
212
216
|
# Same procedure for error mitigated data
|
|
213
217
|
if dataset.attrs["rem"]:
|
|
214
|
-
probs_mit = xrvariable_to_counts(dataset, f"{
|
|
215
|
-
# for u in range(num_circuits):
|
|
216
|
-
# probs_mit.append(
|
|
217
|
-
# dict(
|
|
218
|
-
# zip(
|
|
219
|
-
# list(dataset[f"{str(qubit_layout)}_rem_state_{u}"].data),
|
|
220
|
-
# dataset[f"{str(qubit_layout)}_rem_counts_{u}"].data,
|
|
221
|
-
# )
|
|
222
|
-
# )
|
|
223
|
-
# )
|
|
218
|
+
probs_mit = xrvariable_to_counts(dataset, f"{idx}_rem", num_circuits)
|
|
224
219
|
all_zero_probability_list_mit = []
|
|
225
220
|
for prob in probs_mit[1:]:
|
|
226
221
|
if "0" * num_qubits in prob.keys():
|
|
@@ -257,26 +252,28 @@ def fidelity_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
|
|
|
257
252
|
for qubit_layout in qubit_layouts:
|
|
258
253
|
if routine == "randomized_measurements":
|
|
259
254
|
ideal_simulator = Aer.get_backend("statevector_simulator")
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
255
|
+
ideal_probabilities = []
|
|
256
|
+
idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
257
|
+
all_circuits = run.dataset.attrs["transpiled_circuits"][idx]
|
|
258
|
+
for qc in all_circuits:
|
|
259
|
+
qc_copy = qc.copy()
|
|
260
|
+
qc_copy.remove_final_measurements()
|
|
261
|
+
deflated_qc = reduce_to_active_qubits(qc_copy, backend_name)
|
|
262
|
+
ideal_probabilities.append(dict(sorted(ideal_simulator.run(deflated_qc).result().get_counts().items())))
|
|
263
|
+
values, uncertainties = fidelity_ghz_randomized_measurements(
|
|
264
|
+
dataset, qubit_layout, ideal_probabilities, len(qubit_layout)
|
|
265
|
+
)
|
|
266
|
+
observation_list.extend(
|
|
267
|
+
[
|
|
268
|
+
BenchmarkObservation(
|
|
269
|
+
name=key,
|
|
270
|
+
identifier=BenchmarkObservationIdentifier(qubit_layout),
|
|
271
|
+
value=value,
|
|
272
|
+
uncertainty=uncertainties[key],
|
|
269
273
|
)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
name=key, identifier=BenchmarkObservationIdentifier(qubit_layout), value=value
|
|
274
|
-
)
|
|
275
|
-
for key, value in fidelity_ghz_randomized_measurements(
|
|
276
|
-
dataset, qubit_layout, ideal_probabilities, len(qubit_layout)
|
|
277
|
-
).items()
|
|
278
|
-
]
|
|
279
|
-
)
|
|
274
|
+
for key, value in values.items()
|
|
275
|
+
]
|
|
276
|
+
)
|
|
280
277
|
else: # default routine == "coherences":
|
|
281
278
|
fidelity = fidelity_ghz_coherences(dataset, qubit_layout)
|
|
282
279
|
observation_list.extend(
|
|
@@ -287,13 +284,13 @@ def fidelity_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
|
|
|
287
284
|
]
|
|
288
285
|
)
|
|
289
286
|
if len(fidelity) > 1:
|
|
290
|
-
|
|
291
287
|
observation_list.append(
|
|
292
288
|
BenchmarkObservation(
|
|
293
289
|
name="fidelity_rem", identifier=BenchmarkObservationIdentifier(qubit_layout), value=fidelity[1]
|
|
294
290
|
)
|
|
295
291
|
)
|
|
296
|
-
|
|
292
|
+
plots = {"All layout fidelities": plot_fidelities(observation_list, qubit_layouts)}
|
|
293
|
+
return BenchmarkAnalysisResult(dataset=dataset, observations=observation_list, plots=plots)
|
|
297
294
|
|
|
298
295
|
|
|
299
296
|
def generate_ghz_linear(num_qubits: int) -> QuantumCircuit:
|
|
@@ -442,18 +439,23 @@ def extract_fidelities(cal_url: str, qubit_layout: List[int]) -> Tuple[List[List
|
|
|
442
439
|
return list_couplings, list_fids
|
|
443
440
|
|
|
444
441
|
|
|
445
|
-
def get_edges(
|
|
442
|
+
def get_edges(
|
|
443
|
+
coupling_map: CouplingMap,
|
|
444
|
+
qubit_layout: List[int],
|
|
445
|
+
edges_cal: Optional[List[List[int]]] = None,
|
|
446
|
+
fidelities_cal: Optional[List[float]] = None,
|
|
447
|
+
):
|
|
446
448
|
"""Produces a networkx.Graph from coupling map fidelity information, with edges given by couplings
|
|
447
449
|
and edge weights given by fidelities
|
|
448
450
|
|
|
449
451
|
Args:
|
|
450
|
-
coupling_map:
|
|
452
|
+
coupling_map (CouplingMap):
|
|
451
453
|
The list pairs on which 2-qubit gates are natively supported
|
|
452
|
-
qubit_layout
|
|
454
|
+
qubit_layout (List[int]):
|
|
453
455
|
The subset of system-qubits used in the protocol, indexed from 0
|
|
454
|
-
edges_cal
|
|
455
|
-
|
|
456
|
-
fidelities_cal
|
|
456
|
+
edges_cal (Optional[List[List[int]]]):
|
|
457
|
+
A coupling map of qubit pairs that have CZ fidelities in the calibration data
|
|
458
|
+
fidelities_cal (Optional[List[float]]):
|
|
457
459
|
A list of CZ fidelities ordered in the same way as edges_cal
|
|
458
460
|
|
|
459
461
|
Returns:
|
|
@@ -466,16 +468,18 @@ def get_edges(coupling_map, qubit_layout, edges_cal=None, fidelities_cal=None):
|
|
|
466
468
|
if edge[0] in qubit_layout and edge[1] in qubit_layout:
|
|
467
469
|
edges_patch.append([edge[0], edge[1]])
|
|
468
470
|
|
|
469
|
-
if fidelities_cal is None:
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
471
|
+
if fidelities_cal is not None:
|
|
472
|
+
fidelities_cal = list(
|
|
473
|
+
np.minimum(np.array(fidelities_cal), np.ones(len(fidelities_cal)))
|
|
474
|
+
) # get rid of > 1 fidelities
|
|
473
475
|
fidelities_patch = []
|
|
474
476
|
for edge in edges_patch:
|
|
475
|
-
for idx, edge_2 in enumerate(edges_cal):
|
|
477
|
+
for idx, edge_2 in enumerate(cast(List[int], edges_cal)):
|
|
476
478
|
if edge == edge_2:
|
|
477
479
|
fidelities_patch.append(fidelities_cal[idx])
|
|
478
480
|
weights = -np.log(np.array(fidelities_patch))
|
|
481
|
+
else:
|
|
482
|
+
weights = np.ones(len(edges_patch))
|
|
479
483
|
graph = Graph()
|
|
480
484
|
for idx, edge in enumerate(edges_patch):
|
|
481
485
|
graph.add_edge(*edge, weight=weights[idx])
|
|
@@ -484,7 +488,7 @@ def get_edges(coupling_map, qubit_layout, edges_cal=None, fidelities_cal=None):
|
|
|
484
488
|
return graph
|
|
485
489
|
|
|
486
490
|
|
|
487
|
-
def get_cx_map(qubit_layout, graph) -> list[list[int]]:
|
|
491
|
+
def get_cx_map(qubit_layout: List[int], graph: networkx.Graph) -> list[list[int]]:
|
|
488
492
|
"""Calculate the cx_map based on participating qubits and the 2QB gate fidelities between them.
|
|
489
493
|
|
|
490
494
|
Uses networkx graph algorithms to calculate the minimal spanning tree of the subgraph defined by qubit_layout.
|
|
@@ -525,8 +529,56 @@ def get_cx_map(qubit_layout, graph) -> list[list[int]]:
|
|
|
525
529
|
return cx_map
|
|
526
530
|
|
|
527
531
|
|
|
532
|
+
def plot_fidelities(observations: List[BenchmarkObservation], qubit_layouts: List[List[int]]) -> Figure:
|
|
533
|
+
"""Plots all the fidelities stored in the observations into a single plot of fidelity vs. number of qubits
|
|
534
|
+
|
|
535
|
+
Parameters
|
|
536
|
+
----------
|
|
537
|
+
observations: List[BenchmarkObservation]
|
|
538
|
+
A list of Observations, each assumed to be a fidelity
|
|
539
|
+
qubit_layouts
|
|
540
|
+
The list of qubit layouts as given by the user. This is used to name the layouts in order for identification
|
|
541
|
+
in the plot.
|
|
542
|
+
Returns
|
|
543
|
+
-------
|
|
544
|
+
fig :Figure
|
|
545
|
+
The figure object with the fidelity plot.
|
|
546
|
+
"""
|
|
547
|
+
fig, ax = plt.subplots()
|
|
548
|
+
layout_short = {str(qubit_layout): f" L{i}" for i, qubit_layout in enumerate(qubit_layouts)}
|
|
549
|
+
recorded_labels = []
|
|
550
|
+
for i, obs in enumerate(observations):
|
|
551
|
+
label = "With REM" if "rem" in obs.name else "Unmitigated"
|
|
552
|
+
if label in recorded_labels:
|
|
553
|
+
label = "_nolegend_"
|
|
554
|
+
else:
|
|
555
|
+
recorded_labels.append(label)
|
|
556
|
+
x = sum(c.isdigit() for c in obs.identifier.string_identifier)
|
|
557
|
+
y = obs.value
|
|
558
|
+
ax.errorbar(
|
|
559
|
+
x,
|
|
560
|
+
y,
|
|
561
|
+
yerr=obs.uncertainty,
|
|
562
|
+
capsize=4,
|
|
563
|
+
color="orange" if "rem" in obs.name else "cornflowerblue",
|
|
564
|
+
label=label,
|
|
565
|
+
fmt="o",
|
|
566
|
+
alpha=1,
|
|
567
|
+
markersize=5,
|
|
568
|
+
)
|
|
569
|
+
ax.annotate(layout_short[obs.identifier.string_identifier], (x, y))
|
|
570
|
+
ax.axhline(0.5, linestyle="--", color="black", label="GME threshold")
|
|
571
|
+
# ax.set_ylim([0,1])
|
|
572
|
+
ax.set_title("GHZ fidelities of all qubit layouts")
|
|
573
|
+
ax.set_xlabel("Number of qubits")
|
|
574
|
+
ax.set_ylabel("Fidelity")
|
|
575
|
+
ax.legend(framealpha=0.5)
|
|
576
|
+
plt.close()
|
|
577
|
+
return fig
|
|
578
|
+
|
|
579
|
+
|
|
528
580
|
class GHZBenchmark(Benchmark):
|
|
529
|
-
"""The GHZ Benchmark estimates the quality of generated Greenberger
|
|
581
|
+
"""The GHZ Benchmark estimates the quality of generated Greenberger–Horne–Zeilinger states"""
|
|
530
582
|
|
|
531
583
|
analysis_function = staticmethod(fidelity_analysis)
|
|
532
584
|
name = "ghz"
|
|
@@ -541,28 +593,27 @@ class GHZBenchmark(Benchmark):
|
|
|
541
593
|
super().__init__(backend, configuration)
|
|
542
594
|
|
|
543
595
|
self.state_generation_routine = configuration.state_generation_routine
|
|
544
|
-
self.choose_qubits_routine = configuration.choose_qubits_routine
|
|
596
|
+
# self.choose_qubits_routine = configuration.choose_qubits_routine
|
|
545
597
|
if configuration.custom_qubits_array:
|
|
546
598
|
self.custom_qubits_array = configuration.custom_qubits_array
|
|
547
599
|
else:
|
|
548
|
-
self.custom_qubits_array = list(set(chain(*backend.coupling_map)))
|
|
600
|
+
self.custom_qubits_array = [list(set(chain(*backend.coupling_map)))]
|
|
601
|
+
self.qubit_counts: Sequence[int] | List[int]
|
|
549
602
|
if not configuration.qubit_counts:
|
|
550
603
|
self.qubit_counts = [len(layout) for layout in self.custom_qubits_array]
|
|
551
604
|
else:
|
|
552
605
|
if any(np.max(configuration.qubit_counts) > [len(layout) for layout in self.custom_qubits_array]):
|
|
553
606
|
raise ValueError("The maximum given qubit count is larger than the size of the smallest qubit layout.")
|
|
554
607
|
self.qubit_counts = configuration.qubit_counts
|
|
608
|
+
# self.layout_idx_mapping = {str(qubit_layout): idx for idx, qubit_layout in enumerate(self.custom_qubits_array)}
|
|
555
609
|
|
|
556
610
|
self.qiskit_optim_level = configuration.qiskit_optim_level
|
|
557
611
|
self.optimize_sqg = configuration.optimize_sqg
|
|
558
|
-
|
|
559
612
|
self.fidelity_routine = configuration.fidelity_routine
|
|
560
613
|
self.num_RMs = configuration.num_RMs
|
|
561
|
-
|
|
562
614
|
self.rem = configuration.rem
|
|
563
615
|
self.mit_shots = configuration.mit_shots
|
|
564
616
|
self.cal_url = configuration.cal_url
|
|
565
|
-
|
|
566
617
|
self.timestamp = strftime("%Y%m%d-%H%M%S")
|
|
567
618
|
|
|
568
619
|
# @staticmethod
|
|
@@ -587,10 +638,11 @@ class GHZBenchmark(Benchmark):
|
|
|
587
638
|
"""
|
|
588
639
|
# num_qubits = len(qubit_layout)
|
|
589
640
|
fixed_coupling_map = set_coupling_map(qubit_layout, self.backend, "fixed")
|
|
590
|
-
|
|
641
|
+
idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
642
|
+
ghz_native_transpiled: List[QuantumCircuit]
|
|
591
643
|
if routine == "naive":
|
|
592
644
|
ghz = generate_ghz_linear(qubit_count)
|
|
593
|
-
self.untranspiled_circuits[
|
|
645
|
+
self.untranspiled_circuits[idx].update({qubit_count: ghz})
|
|
594
646
|
ghz_native_transpiled, _ = perform_backend_transpilation(
|
|
595
647
|
[ghz],
|
|
596
648
|
self.backend,
|
|
@@ -607,7 +659,7 @@ class GHZBenchmark(Benchmark):
|
|
|
607
659
|
else:
|
|
608
660
|
graph = get_edges(self.backend.coupling_map, qubit_layout)
|
|
609
661
|
ghz, _ = generate_ghz_spanning_tree(graph, qubit_layout, qubit_count)
|
|
610
|
-
self.untranspiled_circuits[
|
|
662
|
+
self.untranspiled_circuits[idx].update({qubit_count: ghz})
|
|
611
663
|
ghz_native_transpiled, _ = perform_backend_transpilation(
|
|
612
664
|
[ghz],
|
|
613
665
|
self.backend,
|
|
@@ -631,11 +683,11 @@ class GHZBenchmark(Benchmark):
|
|
|
631
683
|
if ghz_native_transpiled[0].depth() == ghz_native_transpiled[1].depth():
|
|
632
684
|
index_min_2q = np.argmin([c.count_ops()["cz"] for c in ghz_native_transpiled])
|
|
633
685
|
final_ghz = ghz_native_transpiled[index_min_2q]
|
|
634
|
-
self.untranspiled_circuits[
|
|
686
|
+
self.untranspiled_circuits[idx].update({qubit_count: ghz_log[index_min_2q]})
|
|
635
687
|
else:
|
|
636
688
|
index_min_depth = np.argmin([c.depth() for c in ghz_native_transpiled])
|
|
637
689
|
final_ghz = ghz_native_transpiled[index_min_depth]
|
|
638
|
-
self.untranspiled_circuits[
|
|
690
|
+
self.untranspiled_circuits[idx].update({qubit_count: ghz_log[index_min_depth]})
|
|
639
691
|
return final_ghz[0]
|
|
640
692
|
|
|
641
693
|
def generate_coherence_meas_circuits(self, qubit_layout: List[int], qubit_count: int) -> List[QuantumCircuit]:
|
|
@@ -653,7 +705,8 @@ class GHZBenchmark(Benchmark):
|
|
|
653
705
|
A list of transpiled quantum circuits to be measured
|
|
654
706
|
"""
|
|
655
707
|
|
|
656
|
-
|
|
708
|
+
idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
709
|
+
qc = self.untranspiled_circuits[idx][qubit_count]
|
|
657
710
|
qc_list = [qc.copy()]
|
|
658
711
|
|
|
659
712
|
qc.remove_final_measurements()
|
|
@@ -678,7 +731,7 @@ class GHZBenchmark(Benchmark):
|
|
|
678
731
|
qiskit_optim_level=self.qiskit_optim_level,
|
|
679
732
|
optimize_sqg=self.optimize_sqg,
|
|
680
733
|
)
|
|
681
|
-
self.untranspiled_circuits[
|
|
734
|
+
self.untranspiled_circuits[idx].update({qubit_count: qc_list})
|
|
682
735
|
return qc_list_transpiled
|
|
683
736
|
|
|
684
737
|
def generate_readout_circuit(self, qubit_layout, qubit_count):
|
|
@@ -697,33 +750,31 @@ class GHZBenchmark(Benchmark):
|
|
|
697
750
|
A list of transpiled quantum circuits to be measured
|
|
698
751
|
"""
|
|
699
752
|
# Generate the list of circuits
|
|
700
|
-
|
|
701
|
-
self.
|
|
753
|
+
idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
754
|
+
self.untranspiled_circuits[idx] = {}
|
|
755
|
+
self.transpiled_circuits[idx] = {}
|
|
702
756
|
|
|
703
757
|
qcvv_logger.info(f"Now generating a {len(qubit_layout)}-qubit GHZ state on qubits {qubit_layout}")
|
|
704
758
|
transpiled_ghz = self.generate_native_ghz(qubit_layout, qubit_count, self.state_generation_routine)
|
|
705
759
|
|
|
706
760
|
if self.fidelity_routine == "randomized_measurements":
|
|
707
761
|
all_circuits_list, _ = append_rms(transpiled_ghz, cast(int, self.num_RMs), self.backend)
|
|
708
|
-
all_circuits_dict = {tuple(qubit_layout): all_circuits_list}
|
|
709
762
|
elif self.fidelity_routine == "coherences":
|
|
710
763
|
all_circuits_list = self.generate_coherence_meas_circuits(qubit_layout, qubit_count)
|
|
711
|
-
all_circuits_dict = {tuple(qubit_layout): all_circuits_list}
|
|
712
764
|
else:
|
|
713
765
|
all_circuits_list = transpiled_ghz
|
|
714
|
-
all_circuits_dict = {tuple(qubit_layout): all_circuits_list}
|
|
715
766
|
|
|
716
|
-
self.transpiled_circuits
|
|
767
|
+
self.transpiled_circuits.update({idx: all_circuits_list})
|
|
717
768
|
return all_circuits_list
|
|
718
769
|
|
|
719
|
-
def add_configuration_to_dataset(self, dataset): # CHECK
|
|
770
|
+
def add_configuration_to_dataset(self, dataset: xr.Dataset): # CHECK
|
|
720
771
|
"""
|
|
721
|
-
Creates
|
|
772
|
+
Creates a xarray.Dataset and adds the circuits and configuration metadata to it.
|
|
722
773
|
|
|
723
774
|
Args:
|
|
724
|
-
|
|
775
|
+
dataset (xr.Dataset):
|
|
725
776
|
Returns:
|
|
726
|
-
|
|
777
|
+
xr.Dataset: dataset to be used for further data storage
|
|
727
778
|
"""
|
|
728
779
|
|
|
729
780
|
for key, value in self.configuration:
|
|
@@ -742,28 +793,38 @@ class GHZBenchmark(Benchmark):
|
|
|
742
793
|
aux_custom_qubits_array = cast(List[List[int]], self.custom_qubits_array).copy()
|
|
743
794
|
dataset = xr.Dataset()
|
|
744
795
|
|
|
796
|
+
# Submit all
|
|
797
|
+
all_jobs: Dict = {}
|
|
745
798
|
for qubit_layout in aux_custom_qubits_array:
|
|
799
|
+
Id = BenchmarkObservationIdentifier(qubit_layout)
|
|
800
|
+
idx = Id.string_identifier
|
|
801
|
+
# for qubit_count in self.qubit_counts[idx]:
|
|
746
802
|
qubit_count = len(qubit_layout)
|
|
747
803
|
circuits = self.generate_readout_circuit(qubit_layout, qubit_count)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
804
|
+
transpiled_circuit_dict = {tuple(qubit_layout): circuits}
|
|
805
|
+
all_jobs[idx], _ = submit_execute(
|
|
806
|
+
transpiled_circuit_dict,
|
|
807
|
+
backend,
|
|
808
|
+
self.shots,
|
|
809
|
+
self.calset_id,
|
|
810
|
+
max_gates_per_batch=self.max_gates_per_batch,
|
|
811
|
+
)
|
|
812
|
+
# Retrieve all
|
|
813
|
+
qcvv_logger.info(f"Retrieving counts and adding counts to dataset...")
|
|
814
|
+
for qubit_layout in aux_custom_qubits_array:
|
|
815
|
+
# for qubit_count in self.qubit_counts[idx]:
|
|
816
|
+
Id = BenchmarkObservationIdentifier(qubit_layout)
|
|
817
|
+
idx = Id.string_identifier
|
|
818
|
+
qubit_count = len(qubit_layout)
|
|
819
|
+
counts, _ = retrieve_all_counts(all_jobs[idx])
|
|
820
|
+
dataset, _ = add_counts_to_dataset(counts, idx, dataset)
|
|
759
821
|
if self.rem:
|
|
760
822
|
qcvv_logger.info(f"Applying readout error mitigation")
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
)
|
|
823
|
+
circuits = self.transpiled_circuits[idx]
|
|
824
|
+
rem_results, _ = apply_readout_error_mitigation(backend, circuits, counts, self.mit_shots)
|
|
764
825
|
rem_results_dist = [counts_mit.nearest_probability_distribution() for counts_mit in rem_results]
|
|
765
|
-
|
|
766
|
-
|
|
826
|
+
dataset, _ = add_counts_to_dataset(rem_results_dist, f"{idx}_rem", dataset)
|
|
827
|
+
|
|
767
828
|
self.add_configuration_to_dataset(dataset)
|
|
768
829
|
return dataset
|
|
769
830
|
|
|
@@ -787,9 +848,10 @@ class GHZConfiguration(BenchmarkConfigurationBase):
|
|
|
787
848
|
custom_qubits_array (Optional[Sequence[Sequence[int]]]): A sequence (e.g., Tuple or List) of sequences of
|
|
788
849
|
physical qubit layouts, as specified by integer labels, where the benchmark is meant to be run.
|
|
789
850
|
* If None, takes all qubits specified in the backend coupling map.
|
|
790
|
-
qubit_counts (Optional[Sequence[int]]): A sequence (e.g., Tuple or List) of integers
|
|
851
|
+
qubit_counts (Optional[Sequence[int]]): CURRENTLY NOT SUPPORTED, A sequence (e.g., Tuple or List) of integers
|
|
852
|
+
denoting number of qubits
|
|
791
853
|
for which the benchmark is meant to be run. The largest qubit count provided here has to be smaller than the
|
|
792
|
-
|
|
854
|
+
smalles given qubit layout.
|
|
793
855
|
qiskit_optim_level (int): The optimization level used for transpilation to backend architecture.
|
|
794
856
|
* Default: 3
|
|
795
857
|
optimize_sqg (bool): Whether consecutive single qubit gates are optimized for reduced gate count via
|
|
@@ -814,13 +876,13 @@ class GHZConfiguration(BenchmarkConfigurationBase):
|
|
|
814
876
|
|
|
815
877
|
benchmark: Type[Benchmark] = GHZBenchmark
|
|
816
878
|
state_generation_routine: str = "tree"
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
879
|
+
custom_qubits_array: Optional[Sequence[Sequence[int]]] = None
|
|
880
|
+
qubit_counts: Optional[Sequence[int]] = None
|
|
881
|
+
shots: int = 2**10
|
|
820
882
|
qiskit_optim_level: int = 3
|
|
821
883
|
optimize_sqg: bool = True
|
|
822
884
|
fidelity_routine: str = "coherences"
|
|
823
|
-
num_RMs: Optional[int] =
|
|
885
|
+
num_RMs: Optional[int] = 100
|
|
824
886
|
rem: bool = True
|
|
825
887
|
mit_shots: int = 1_000
|
|
826
888
|
cal_url: Optional[str] = None
|
|
Binary file
|
|
Binary file
|
|
@@ -650,7 +650,6 @@ def plot_rb_decay(
|
|
|
650
650
|
decay_rate[rb_type] = {
|
|
651
651
|
str(q): dataset.attrs[q_idx][rb_type]["decay_rate"]["value"] for q_idx, q in enumerate(qubits_array)
|
|
652
652
|
}
|
|
653
|
-
print(dataset.attrs)
|
|
654
653
|
offset[rb_type] = {
|
|
655
654
|
str(q): dataset.attrs[q_idx][rb_type]["fit_offset"]["value"] for q_idx, q in enumerate(qubits_array)
|
|
656
655
|
}
|
|
@@ -770,11 +769,14 @@ def plot_rb_decay(
|
|
|
770
769
|
fidelity_stderr[key][str(qubits)] = np.nan
|
|
771
770
|
|
|
772
771
|
if identifier == "mrb":
|
|
773
|
-
plot_label = fr"$\overline{{F}}_
|
|
772
|
+
plot_label = fr"$\overline{{F}}_{{MRB}} (n={len(qubits)})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
|
|
774
773
|
elif key == "interleaved":
|
|
775
|
-
plot_label = fr"$\overline{{F}}_
|
|
774
|
+
plot_label = fr"$\overline{{F}}_{{interleaved_gate}} ({qubits})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
|
|
776
775
|
else:
|
|
777
|
-
|
|
776
|
+
print(fidelity_value)
|
|
777
|
+
print(qubits)
|
|
778
|
+
print(key)
|
|
779
|
+
plot_label = fr"$\overline{{F}}_{{CRB}}$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
|
|
778
780
|
|
|
779
781
|
ax.plot(
|
|
780
782
|
x_linspace,
|
|
@@ -25,7 +25,7 @@ from mthree.classes import QuasiCollection
|
|
|
25
25
|
from mthree.exceptions import M3Error
|
|
26
26
|
from mthree.mitigation import _job_thread
|
|
27
27
|
from mthree.utils import final_measurement_mapping
|
|
28
|
-
from qiskit import QuantumCircuit,
|
|
28
|
+
from qiskit import QuantumCircuit, transpile # pylint: disable = no-name-in-module
|
|
29
29
|
from qiskit.providers import Backend, BackendV1, BackendV2
|
|
30
30
|
|
|
31
31
|
from iqm.benchmarks.utils import get_iqm_backend, timeit
|
|
@@ -201,13 +201,8 @@ class M3IQM(mthree.M3Mitigation):
|
|
|
201
201
|
jobs = []
|
|
202
202
|
if not isinstance(self.system, Backend):
|
|
203
203
|
for circs in circs_list:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
self.system,
|
|
207
|
-
optimization_level=0,
|
|
208
|
-
shots=shots,
|
|
209
|
-
rep_delay=self.rep_delay,
|
|
210
|
-
)
|
|
204
|
+
transpiled_circuit = transpile(circs, self.system, optimization_level=0)
|
|
205
|
+
_job = self.system.run(transpiled_circuit, shots=shots, rep_delay=self.rep_delay)
|
|
211
206
|
jobs.append(_job)
|
|
212
207
|
|
|
213
208
|
# *****************************************
|