iqm-benchmarks 1.7__py3-none-any.whl → 1.9__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 +177 -116
- iqm/benchmarks/quantum_volume/clops.py +1 -1
- iqm/benchmarks/quantum_volume/quantum_volume.py +20 -18
- iqm/benchmarks/utils.py +0 -31
- {iqm_benchmarks-1.7.dist-info → iqm_benchmarks-1.9.dist-info}/METADATA +14 -7
- {iqm_benchmarks-1.7.dist-info → iqm_benchmarks-1.9.dist-info}/RECORD +11 -10
- {iqm_benchmarks-1.7.dist-info → iqm_benchmarks-1.9.dist-info}/LICENSE +0 -0
- {iqm_benchmarks-1.7.dist-info → iqm_benchmarks-1.9.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-1.7.dist-info → iqm_benchmarks-1.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data analysis code for compressive gate set tomography
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from itertools import product
|
|
6
|
+
from time import perf_counter
|
|
7
|
+
from typing import Any, List, Tuple, Union
|
|
8
|
+
|
|
9
|
+
from matplotlib.figure import Figure
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from matplotlib.transforms import Bbox
|
|
12
|
+
from numpy import ndarray
|
|
13
|
+
import numpy as np
|
|
14
|
+
from pandas import DataFrame
|
|
15
|
+
from pygsti.models.model import Model
|
|
16
|
+
from pygsti.tools import change_basis
|
|
17
|
+
import xarray as xr
|
|
18
|
+
|
|
19
|
+
from iqm.benchmarks.benchmark_definition import (
|
|
20
|
+
BenchmarkAnalysisResult,
|
|
21
|
+
BenchmarkObservation,
|
|
22
|
+
BenchmarkObservationIdentifier,
|
|
23
|
+
BenchmarkRunResult,
|
|
24
|
+
)
|
|
25
|
+
from mGST import additional_fns, algorithm, compatibility
|
|
26
|
+
from mGST.low_level_jit import contract
|
|
27
|
+
from mGST.qiskit_interface import qiskit_gate_to_operator
|
|
28
|
+
from mGST.reporting import figure_gen, reporting
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def dataframe_to_figure(
|
|
32
|
+
df: DataFrame, row_labels: Union[List[str], None] = None, col_width: float = 2, fontsize: int = 12
|
|
33
|
+
) -> Figure:
|
|
34
|
+
"""Turns a pandas DataFrame into a figure
|
|
35
|
+
This is needed to conform with the standard file saving routine of QCVV.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
df: Pandas DataFrame
|
|
39
|
+
A dataframe table containing GST results
|
|
40
|
+
row_labels: List[str]
|
|
41
|
+
The row labels for the dataframe
|
|
42
|
+
col_width: int
|
|
43
|
+
Used to control cell width in the table
|
|
44
|
+
fontsize: int
|
|
45
|
+
Font size of text/numbers in table cells
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
figure: Matplotlib figure object
|
|
49
|
+
A figure representing the dataframe.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
if row_labels is None:
|
|
53
|
+
row_labels = list(np.arange(df.shape[0]))
|
|
54
|
+
|
|
55
|
+
row_height = fontsize / 70 * 2
|
|
56
|
+
n_cols = df.shape[1]
|
|
57
|
+
n_rows = df.shape[0]
|
|
58
|
+
figsize = np.array([n_cols + 1, n_rows + 1]) * np.array([col_width, row_height])
|
|
59
|
+
|
|
60
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
61
|
+
|
|
62
|
+
fig.patch.set_visible(False)
|
|
63
|
+
ax.axis("off")
|
|
64
|
+
ax.axis("tight")
|
|
65
|
+
data_array = (df.to_numpy(dtype="str")).copy()
|
|
66
|
+
column_names = df.columns.tolist()
|
|
67
|
+
table = ax.table(
|
|
68
|
+
cellText=data_array,
|
|
69
|
+
colLabels=column_names,
|
|
70
|
+
rowLabels=row_labels,
|
|
71
|
+
cellLoc="center",
|
|
72
|
+
colColours=["#7FA1C3" for _ in range(n_cols)],
|
|
73
|
+
bbox=Bbox([[0, 0], [1, 1]]),
|
|
74
|
+
)
|
|
75
|
+
table.set_fontsize(fontsize)
|
|
76
|
+
table.set_figure(fig)
|
|
77
|
+
return fig
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def bootstrap_errors(
|
|
81
|
+
dataset: xr.Dataset,
|
|
82
|
+
y: ndarray,
|
|
83
|
+
K: ndarray,
|
|
84
|
+
X: ndarray,
|
|
85
|
+
E: ndarray,
|
|
86
|
+
rho: ndarray,
|
|
87
|
+
target_mdl: Model,
|
|
88
|
+
parametric: bool = False,
|
|
89
|
+
) -> tuple[Any, Any, Any, Any, Any]:
|
|
90
|
+
"""Resamples circuit outcomes a number of times and computes GST estimates for each repetition
|
|
91
|
+
All results are then returned in order to compute bootstrap-error bars for GST estimates.
|
|
92
|
+
Parametric bootstrapping uses the estimated gate set to create a newly sampled data set.
|
|
93
|
+
Non-parametric bootstrapping uses the initial dataset and resamples according to the
|
|
94
|
+
corresp. outcome probabilities.
|
|
95
|
+
Each bootstrap run is initialized with the estimated gate set in order to save processing time.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
dataset: xarray.Dataset
|
|
100
|
+
A dataset containing counts from the experiment and configurations
|
|
101
|
+
qubit_layout: List[int]
|
|
102
|
+
The list of qubits for the current GST experiment
|
|
103
|
+
y: ndarray
|
|
104
|
+
The circuit outcome probabilities as a num_povm x num_circuits array
|
|
105
|
+
K : ndarray
|
|
106
|
+
Each subarray along the first axis contains a set of Kraus operators.
|
|
107
|
+
The second axis enumerates Kraus operators for a gate specified by the first axis.
|
|
108
|
+
X : 3D ndarray
|
|
109
|
+
Array where reconstructed CPT superoperators in standard basis are stacked along the first axis.
|
|
110
|
+
E : ndarray
|
|
111
|
+
Current POVM estimate
|
|
112
|
+
rho : ndarray
|
|
113
|
+
Current initial state estimate
|
|
114
|
+
target_mdl : pygsti model object
|
|
115
|
+
The target gate set
|
|
116
|
+
parametric : bool
|
|
117
|
+
If set to True, parametric bootstrapping is used, else non-parametric bootstrapping. Default: False
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
X_array : ndarray
|
|
122
|
+
Array containing all estimated gate tensors of different bootstrapping repetitions along first axis
|
|
123
|
+
E_array : ndarray
|
|
124
|
+
Array containing all estimated POVM tensors of different bootstrapping repetitions along first axis
|
|
125
|
+
rho_array : ndarray
|
|
126
|
+
Array containing all estimated initial states of different bootstrapping repetitions along first axis
|
|
127
|
+
df_g_array : ndarray
|
|
128
|
+
Contains gate quality measures of bootstrapping repetitions
|
|
129
|
+
df_o_array : ndarray
|
|
130
|
+
Contains SPAM and other quality measures of bootstrapping repetitions
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
if parametric:
|
|
134
|
+
y = np.real(
|
|
135
|
+
np.array(
|
|
136
|
+
[
|
|
137
|
+
[E[i].conj() @ contract(X, j) @ rho for j in dataset.attrs["J"]]
|
|
138
|
+
for i in range(dataset.attrs["num_povm"])
|
|
139
|
+
]
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
X_array = np.zeros((dataset.attrs["bootstrap_samples"], *X.shape)).astype(complex)
|
|
143
|
+
E_array = np.zeros((dataset.attrs["bootstrap_samples"], *E.shape)).astype(complex)
|
|
144
|
+
rho_array = np.zeros((dataset.attrs["bootstrap_samples"], *rho.shape)).astype(complex)
|
|
145
|
+
df_g_list = []
|
|
146
|
+
df_o_list = []
|
|
147
|
+
|
|
148
|
+
for i in range(dataset.attrs["bootstrap_samples"]):
|
|
149
|
+
y_sampled = additional_fns.sampled_measurements(y, dataset.attrs["shots"]).copy()
|
|
150
|
+
_, X_, E_, rho_, _ = algorithm.run_mGST(
|
|
151
|
+
y_sampled,
|
|
152
|
+
dataset.attrs["J"],
|
|
153
|
+
dataset.attrs["seq_len_list"][-1],
|
|
154
|
+
dataset.attrs["num_gates"],
|
|
155
|
+
dataset.attrs["pdim"] ** 2,
|
|
156
|
+
dataset.attrs["rank"],
|
|
157
|
+
dataset.attrs["num_povm"],
|
|
158
|
+
dataset.attrs["batch_size"],
|
|
159
|
+
dataset.attrs["shots"],
|
|
160
|
+
method=dataset.attrs["opt_method"],
|
|
161
|
+
max_inits=dataset.attrs["max_inits"],
|
|
162
|
+
max_iter=0,
|
|
163
|
+
final_iter=dataset.attrs["max_iterations"][1],
|
|
164
|
+
threshold_multiplier=dataset.attrs["convergence_criteria"][0],
|
|
165
|
+
target_rel_prec=dataset.attrs["convergence_criteria"][1],
|
|
166
|
+
init=[K, E, rho],
|
|
167
|
+
testing=False,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
X_opt, E_opt, rho_opt = reporting.gauge_opt(X_, E_, rho_, target_mdl, dataset.attrs[f"gauge_weights"])
|
|
171
|
+
df_g, df_o = reporting.report(
|
|
172
|
+
X_opt,
|
|
173
|
+
E_opt,
|
|
174
|
+
rho_opt,
|
|
175
|
+
dataset.attrs["J"],
|
|
176
|
+
y_sampled,
|
|
177
|
+
target_mdl,
|
|
178
|
+
dataset.attrs["gate_labels"],
|
|
179
|
+
)
|
|
180
|
+
df_g_list.append(df_g.values)
|
|
181
|
+
df_o_list.append(df_o.values)
|
|
182
|
+
|
|
183
|
+
X_opt_pp, E_opt_pp, rho_opt_pp = compatibility.std2pp(X_opt, E_opt, rho_opt)
|
|
184
|
+
|
|
185
|
+
X_array[i] = X_opt_pp
|
|
186
|
+
E_array[i] = E_opt_pp
|
|
187
|
+
rho_array[i] = rho_opt_pp
|
|
188
|
+
|
|
189
|
+
return X_array, E_array, rho_array, np.array(df_g_list), np.array(df_o_list)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def generate_non_gate_results(
|
|
193
|
+
dataset: xr.Dataset, qubit_layout: List[int], df_o: DataFrame
|
|
194
|
+
) -> tuple[DataFrame, Figure]:
|
|
195
|
+
"""
|
|
196
|
+
Creates error bars (if bootstrapping was used) and formats results for non-gate errors.
|
|
197
|
+
The resulting tables are also turned into figures, so that they can be saved automatically.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
dataset: xr.Dataset
|
|
201
|
+
A dataset containing counts from the experiment and configurations
|
|
202
|
+
qubit_layout: List[int]
|
|
203
|
+
The list of qubits for the current GST experiment
|
|
204
|
+
df_o: Pandas DataFrame
|
|
205
|
+
A dataframe containing the non-gate quality metrics (SPAM errors and fit quality)
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
df_o_final: Pandas DataFrame
|
|
209
|
+
The final formated results
|
|
210
|
+
"""
|
|
211
|
+
identifier = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
212
|
+
if dataset.attrs["bootstrap_samples"] > 0:
|
|
213
|
+
_, _, _, _, df_o_array = dataset.attrs["results_layout_" + identifier]["bootstrap_data"]
|
|
214
|
+
df_o_array[df_o_array == -1] = np.nan
|
|
215
|
+
percentiles_o_low, percentiles_o_high = np.nanpercentile(df_o_array, [2.5, 97.5], axis=0)
|
|
216
|
+
df_o_final = DataFrame(
|
|
217
|
+
{
|
|
218
|
+
f"Mean TVD: estimate - data": reporting.number_to_str(
|
|
219
|
+
df_o.values[0, 1].copy(), [percentiles_o_high[0, 1], percentiles_o_low[0, 1]], precision=5
|
|
220
|
+
),
|
|
221
|
+
f"Mean TVD: target - data": reporting.number_to_str(
|
|
222
|
+
df_o.values[0, 2].copy(), [percentiles_o_high[0, 2], percentiles_o_low[0, 2]], precision=5
|
|
223
|
+
),
|
|
224
|
+
f"POVM - diamond dist.": reporting.number_to_str(
|
|
225
|
+
df_o.values[0, 3].copy(), [percentiles_o_high[0, 3], percentiles_o_low[0, 3]], precision=5
|
|
226
|
+
),
|
|
227
|
+
f"State - trace dist.": reporting.number_to_str(
|
|
228
|
+
df_o.values[0, 4].copy(), [percentiles_o_high[0, 4], percentiles_o_low[0, 4]], precision=5
|
|
229
|
+
),
|
|
230
|
+
},
|
|
231
|
+
index=[""],
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
df_o_final = DataFrame(
|
|
235
|
+
{
|
|
236
|
+
f"Mean TVD: estimate - data": reporting.number_to_str(df_o.values[0, 1].copy(), precision=5),
|
|
237
|
+
f"Mean TVD: target - data": reporting.number_to_str(df_o.values[0, 2].copy(), precision=5),
|
|
238
|
+
f"POVM - diamond dist.": reporting.number_to_str(df_o.values[0, 3].copy(), precision=5),
|
|
239
|
+
f"State - trace dist.": reporting.number_to_str(df_o.values[0, 4].copy(), precision=5),
|
|
240
|
+
},
|
|
241
|
+
index=[""],
|
|
242
|
+
)
|
|
243
|
+
fig = dataframe_to_figure(df_o_final, [""]) # dataframe_to_figure(df_o_final, [""])
|
|
244
|
+
return df_o_final, fig
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def generate_unit_rank_gate_results(
|
|
248
|
+
dataset: xr.Dataset, qubit_layout: List[int], df_g: DataFrame, X_opt: ndarray, K_target: ndarray
|
|
249
|
+
) -> Tuple[DataFrame, DataFrame, Figure, Figure]:
|
|
250
|
+
"""
|
|
251
|
+
Produces all result tables for Kraus rank 1 estimates and turns them into figures.
|
|
252
|
+
|
|
253
|
+
This includes parameters of the Hamiltonian generators in the Pauli basis for all gates,
|
|
254
|
+
as well as the usual performance metrics (Fidelities and Diamond distances). If bootstrapping
|
|
255
|
+
data is available, error bars will also be generated.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
dataset: xarray.Dataset
|
|
259
|
+
A dataset containing counts from the experiment and configurations
|
|
260
|
+
qubit_layout: List[int]
|
|
261
|
+
The list of qubits for the current GST experiment
|
|
262
|
+
df_g: Pandas DataFrame
|
|
263
|
+
The dataframe with properly formated results
|
|
264
|
+
X_opt: 3D numpy array
|
|
265
|
+
The gate set after gauge optimization
|
|
266
|
+
K_target: 4D numpy array
|
|
267
|
+
The Kraus operators of all target gates, used to compute distance measures.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
df_g_final: Pandas DataFrame
|
|
271
|
+
The dataframe with properly formated results of standard gate errors
|
|
272
|
+
df_g_rotation Pandas DataFrame
|
|
273
|
+
A dataframe containing Hamiltonian (rotation) parameters
|
|
274
|
+
fig_g: Figure
|
|
275
|
+
A table in Figure format of gate results (fidelities etc.)
|
|
276
|
+
fig_rotation: Figure
|
|
277
|
+
A table in Figure format of gate Hamiltonian parameters
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
"""
|
|
281
|
+
identifier = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
282
|
+
pauli_labels = generate_basis_labels(dataset.attrs["pdim"], basis="Pauli")
|
|
283
|
+
if dataset.attrs["bootstrap_samples"] > 0:
|
|
284
|
+
X_array, E_array, rho_array, df_g_array, _ = dataset.attrs["results_layout_" + identifier]["bootstrap_data"]
|
|
285
|
+
df_g_array[df_g_array == -1] = np.nan
|
|
286
|
+
percentiles_g_low, percentiles_g_high = np.nanpercentile(df_g_array, [2.5, 97.5], axis=0)
|
|
287
|
+
|
|
288
|
+
df_g_final = DataFrame(
|
|
289
|
+
{
|
|
290
|
+
r"Avg. gate fidelity": [
|
|
291
|
+
reporting.number_to_str(
|
|
292
|
+
df_g.values[i, 0], [percentiles_g_high[i, 0], percentiles_g_low[i, 0]], precision=5
|
|
293
|
+
)
|
|
294
|
+
for i in range(len(dataset.attrs["gate_labels"]))
|
|
295
|
+
],
|
|
296
|
+
r"Diamond distance": [
|
|
297
|
+
reporting.number_to_str(
|
|
298
|
+
df_g.values[i, 1], [percentiles_g_high[i, 1], percentiles_g_low[i, 1]], precision=5
|
|
299
|
+
)
|
|
300
|
+
for i in range(dataset.attrs["num_gates"])
|
|
301
|
+
],
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
U_opt = reporting.phase_opt(X_opt, K_target)
|
|
306
|
+
pauli_coeffs = reporting.compute_sparsest_Pauli_Hamiltonian(U_opt)
|
|
307
|
+
|
|
308
|
+
bootstrap_pauli_coeffs = np.zeros((len(X_array), dataset.attrs["num_gates"], dataset.attrs["pdim"] ** 2))
|
|
309
|
+
for i, X_ in enumerate(X_array):
|
|
310
|
+
X_std, _, _ = compatibility.pp2std(X_, E_array[i], rho_array[i])
|
|
311
|
+
U_opt_ = reporting.phase_opt(
|
|
312
|
+
np.array([change_basis(X_std[j], "pp", "std") for j in range(dataset.attrs["num_gates"])]), K_target
|
|
313
|
+
)
|
|
314
|
+
pauli_coeffs_ = reporting.compute_sparsest_Pauli_Hamiltonian(U_opt_)
|
|
315
|
+
bootstrap_pauli_coeffs[i, :, :] = pauli_coeffs_
|
|
316
|
+
pauli_coeffs_low, pauli_coeffs_high = np.nanpercentile(bootstrap_pauli_coeffs, [2.5, 97.5], axis=0)
|
|
317
|
+
|
|
318
|
+
df_g_rotation = DataFrame(
|
|
319
|
+
np.array(
|
|
320
|
+
[
|
|
321
|
+
[
|
|
322
|
+
reporting.number_to_str(
|
|
323
|
+
pauli_coeffs[i, j], [pauli_coeffs_high[i, j], pauli_coeffs_low[i, j]], precision=5
|
|
324
|
+
)
|
|
325
|
+
for i in range(dataset.attrs["num_gates"])
|
|
326
|
+
]
|
|
327
|
+
for j in range(dataset.attrs["pdim"] ** 2)
|
|
328
|
+
]
|
|
329
|
+
).T
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
df_g_rotation.columns = [f"h_%s" % label for label in pauli_labels]
|
|
333
|
+
df_g_rotation.rename(index=dataset.attrs["gate_labels"], inplace=True)
|
|
334
|
+
|
|
335
|
+
else:
|
|
336
|
+
df_g_final = DataFrame(
|
|
337
|
+
{
|
|
338
|
+
"Avg. gate fidelity": [
|
|
339
|
+
reporting.number_to_str(df_g.values[i, 0], precision=5) for i in range(dataset.attrs["num_gates"])
|
|
340
|
+
],
|
|
341
|
+
"Diamond distance": [
|
|
342
|
+
reporting.number_to_str(df_g.values[i, 1], precision=5) for i in range(dataset.attrs["num_gates"])
|
|
343
|
+
],
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
U_opt = reporting.phase_opt(X_opt, K_target)
|
|
347
|
+
pauli_coeffs = reporting.compute_sparsest_Pauli_Hamiltonian(U_opt)
|
|
348
|
+
|
|
349
|
+
df_g_rotation = DataFrame(
|
|
350
|
+
np.array(
|
|
351
|
+
[
|
|
352
|
+
[
|
|
353
|
+
reporting.number_to_str(pauli_coeffs[i, j], precision=5)
|
|
354
|
+
for i in range(dataset.attrs["num_gates"])
|
|
355
|
+
]
|
|
356
|
+
for j in range(dataset.attrs["pdim"] ** 2)
|
|
357
|
+
]
|
|
358
|
+
).T
|
|
359
|
+
)
|
|
360
|
+
df_g_rotation.columns = [f"h_%s" % label for label in pauli_labels]
|
|
361
|
+
df_g_final.rename(index=dataset.attrs["gate_labels"], inplace=True)
|
|
362
|
+
|
|
363
|
+
fig_g = dataframe_to_figure(df_g_final, dataset.attrs["gate_labels"])
|
|
364
|
+
fig_rotation = dataframe_to_figure(df_g_rotation, dataset.attrs["gate_labels"])
|
|
365
|
+
return df_g_final, df_g_rotation, fig_g, fig_rotation
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def generate_gate_results(
|
|
369
|
+
dataset: xr.Dataset,
|
|
370
|
+
qubit_layout: List[int],
|
|
371
|
+
df_g: DataFrame,
|
|
372
|
+
X_opt: ndarray,
|
|
373
|
+
E_opt: ndarray,
|
|
374
|
+
rho_opt: ndarray,
|
|
375
|
+
max_evals: int = 6,
|
|
376
|
+
) -> Tuple[DataFrame, DataFrame, Figure, Figure]:
|
|
377
|
+
"""
|
|
378
|
+
Produces all result tables for arbitrary Kraus rank estimates and turns them into figures.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
df_g: Pandas DataFrame
|
|
382
|
+
The dataframe with properly formated results
|
|
383
|
+
X_opt: 3D numpy array
|
|
384
|
+
The gate set after gauge optimization
|
|
385
|
+
E_opt: 3D numpy array
|
|
386
|
+
An array containg all the POVM elements as matrices after gauge optimization
|
|
387
|
+
rho_opt: 2D numpy array
|
|
388
|
+
The density matrix after gauge optmization
|
|
389
|
+
max_evals: int
|
|
390
|
+
The maximum number of eigenvalues of the Choi matrices which are returned.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
df_g_final: Pandas DataFrame
|
|
394
|
+
The dataframe with properly formated results of standard gate errors
|
|
395
|
+
df_g_evals_final Pandas DataFrame
|
|
396
|
+
A dataframe containing eigenvalues of the Choi matrices for all gates
|
|
397
|
+
fig_g: Figure
|
|
398
|
+
A table in Figure format of gate results (fidelities etc.)
|
|
399
|
+
fig_choi: Figure
|
|
400
|
+
A table in Figure format of eigenvalues of the Choi matrices of all gates
|
|
401
|
+
|
|
402
|
+
"""
|
|
403
|
+
identifier = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
404
|
+
n_evals = np.min([max_evals, dataset.attrs["pdim"] ** 2])
|
|
405
|
+
X_opt_pp, _, _ = compatibility.std2pp(X_opt, E_opt, rho_opt)
|
|
406
|
+
df_g_evals = reporting.generate_Choi_EV_table(X_opt, n_evals, dataset.attrs["gate_labels"])
|
|
407
|
+
|
|
408
|
+
if dataset.attrs["bootstrap_samples"] > 0:
|
|
409
|
+
X_array, E_array, rho_array, df_g_array, _ = dataset.attrs["results_layout_" + identifier]["bootstrap_data"]
|
|
410
|
+
df_g_array[df_g_array == -1] = np.nan
|
|
411
|
+
percentiles_g_low, percentiles_g_high = np.nanpercentile(df_g_array, [2.5, 97.5], axis=0)
|
|
412
|
+
bootstrap_unitarities = np.array(
|
|
413
|
+
[reporting.unitarities(X_array[i]) for i in range(dataset.attrs["bootstrap_samples"])]
|
|
414
|
+
)
|
|
415
|
+
percentiles_u_low, percentiles_u_high = np.nanpercentile(bootstrap_unitarities, [2.5, 97.5], axis=0)
|
|
416
|
+
X_array_std = [
|
|
417
|
+
compatibility.pp2std(X_array[i], E_array[i], rho_array[i])[0]
|
|
418
|
+
for i in range(dataset.attrs["bootstrap_samples"])
|
|
419
|
+
]
|
|
420
|
+
bootstrap_evals = np.array(
|
|
421
|
+
[
|
|
422
|
+
reporting.generate_Choi_EV_table(X_array_std[i], n_evals, dataset.attrs["gate_labels"])
|
|
423
|
+
for i in range(dataset.attrs["bootstrap_samples"])
|
|
424
|
+
]
|
|
425
|
+
)
|
|
426
|
+
percentiles_evals_low, percentiles_evals_high = np.nanpercentile(bootstrap_evals, [2.5, 97.5], axis=0)
|
|
427
|
+
eval_strs = [
|
|
428
|
+
[
|
|
429
|
+
reporting.number_to_str(
|
|
430
|
+
df_g_evals.values[i, j],
|
|
431
|
+
[percentiles_evals_high[i, j], percentiles_evals_low[i, j]],
|
|
432
|
+
precision=5,
|
|
433
|
+
)
|
|
434
|
+
for i in range(dataset.attrs["num_gates"])
|
|
435
|
+
]
|
|
436
|
+
for j in range(n_evals)
|
|
437
|
+
]
|
|
438
|
+
|
|
439
|
+
df_g_final = DataFrame(
|
|
440
|
+
{
|
|
441
|
+
r"Avg. gate fidelity": [
|
|
442
|
+
reporting.number_to_str(
|
|
443
|
+
df_g.values[i, 0], [percentiles_g_high[i, 0], percentiles_g_low[i, 0]], precision=5
|
|
444
|
+
)
|
|
445
|
+
for i in range(dataset.attrs["num_gates"])
|
|
446
|
+
],
|
|
447
|
+
r"Diamond distance": [
|
|
448
|
+
reporting.number_to_str(
|
|
449
|
+
df_g.values[i, 1], [percentiles_g_high[i, 1], percentiles_g_low[i, 1]], precision=5
|
|
450
|
+
)
|
|
451
|
+
for i in range(dataset.attrs["num_gates"])
|
|
452
|
+
],
|
|
453
|
+
r"Unitarity": [
|
|
454
|
+
reporting.number_to_str(
|
|
455
|
+
reporting.unitarities(X_opt_pp)[i],
|
|
456
|
+
[percentiles_u_high[i], percentiles_u_low[i]],
|
|
457
|
+
precision=5,
|
|
458
|
+
)
|
|
459
|
+
for i in range(dataset.attrs["num_gates"])
|
|
460
|
+
],
|
|
461
|
+
}
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
else:
|
|
465
|
+
df_g_final = DataFrame(
|
|
466
|
+
{
|
|
467
|
+
"Avg. gate fidelity": [
|
|
468
|
+
reporting.number_to_str(df_g.values[i, 0].copy(), precision=5)
|
|
469
|
+
for i in range(len(dataset.attrs["gate_labels"]))
|
|
470
|
+
],
|
|
471
|
+
"Diamond distance": [
|
|
472
|
+
reporting.number_to_str(df_g.values[i, 1].copy(), precision=5)
|
|
473
|
+
for i in range(len(dataset.attrs["gate_labels"]))
|
|
474
|
+
],
|
|
475
|
+
"Unitarity": [
|
|
476
|
+
reporting.number_to_str(reporting.unitarities(X_opt_pp)[i], precision=5)
|
|
477
|
+
for i in range(len(dataset.attrs["gate_labels"]))
|
|
478
|
+
],
|
|
479
|
+
# "Entanglemen fidelity to depol. channel": [reporting.number_to_str(reporting.eff_depol_params(X_opt_pp)[i], precision=5)
|
|
480
|
+
# for i in range(len(gate_labels))],
|
|
481
|
+
# "Min. spectral distances": [number_to_str(df_g.values[i, 2], precision=5) for i in range(len(gate_labels))]
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
eval_strs = [
|
|
485
|
+
[
|
|
486
|
+
reporting.number_to_str(df_g_evals.values[i, j].copy(), precision=5)
|
|
487
|
+
for i in range(dataset.attrs["num_gates"])
|
|
488
|
+
]
|
|
489
|
+
for j in range(n_evals)
|
|
490
|
+
]
|
|
491
|
+
|
|
492
|
+
df_g_evals_final = DataFrame(eval_strs).T
|
|
493
|
+
df_g_evals_final.rename(index=dataset.attrs["gate_labels"], inplace=True)
|
|
494
|
+
|
|
495
|
+
fig_g = dataframe_to_figure(df_g_final, dataset.attrs["gate_labels"])
|
|
496
|
+
fig_choi = dataframe_to_figure(df_g_evals_final, dataset.attrs["gate_labels"])
|
|
497
|
+
return df_g_final, df_g_evals_final, fig_g, fig_choi
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def generate_basis_labels(pdim: int, basis: Union[str, None] = None) -> List[str]:
|
|
501
|
+
"""Generate a list of labels for the Pauli basis or the standard basis
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
pdim: int
|
|
505
|
+
Physical dimension
|
|
506
|
+
basis: str
|
|
507
|
+
Which basis the labels correspond to, currently default is standard basis and "Pauli" can be choose
|
|
508
|
+
for Pauli basis labels like "II", "IX", "XX", ...
|
|
509
|
+
|
|
510
|
+
Returns:
|
|
511
|
+
labels: List[str]
|
|
512
|
+
A list of all string combinations for the given dimension and basis
|
|
513
|
+
"""
|
|
514
|
+
separator = ""
|
|
515
|
+
if basis == "Pauli":
|
|
516
|
+
pauli_labels_loc = ["I", "X", "Y", "Z"]
|
|
517
|
+
pauli_labels_rep = [pauli_labels_loc for _ in range(int(np.log2(pdim)))]
|
|
518
|
+
labels = [separator.join(map(str, x)) for x in product(*pauli_labels_rep)]
|
|
519
|
+
else:
|
|
520
|
+
std_labels_loc = ["0", "1"]
|
|
521
|
+
std_labels_rep = [std_labels_loc for _ in range(int(np.log2(pdim)))]
|
|
522
|
+
labels = [separator.join(map(str, x)) for x in product(*std_labels_rep)]
|
|
523
|
+
|
|
524
|
+
return labels
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def result_str_to_floats(result_str: str, err: str) -> Tuple[float, float]:
|
|
528
|
+
"""Converts formated string results from mgst to float (value, uncertainty) pairs
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
result_str: str
|
|
532
|
+
The value of a result parameter formated as str
|
|
533
|
+
err: str
|
|
534
|
+
The error interval of the parameters
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
value: float
|
|
538
|
+
The parameter value as floar
|
|
539
|
+
uncertainty: float
|
|
540
|
+
A single uncertainty value
|
|
541
|
+
"""
|
|
542
|
+
if err:
|
|
543
|
+
value = float(result_str.split("[")[0])
|
|
544
|
+
rest = result_str.split("[")[1].split(",")
|
|
545
|
+
uncertainty = float(rest[1][:-1]) - float(rest[0])
|
|
546
|
+
return value, uncertainty
|
|
547
|
+
return float(result_str), np.NaN
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def pandas_results_to_observations(
|
|
551
|
+
dataset: xr.Dataset, df_g: DataFrame, df_o: DataFrame, identifier: BenchmarkObservationIdentifier
|
|
552
|
+
) -> List[BenchmarkObservation]:
|
|
553
|
+
"""Converts high level GST results from a pandas Dataframe to a simple observation dictionary
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
dataset: xarray.Dataset
|
|
557
|
+
A dataset containing counts from the experiment and configurations
|
|
558
|
+
qubit_layout: List[int]
|
|
559
|
+
The list of qubits for the current GST experiment
|
|
560
|
+
df_g: Pandas DataFrame
|
|
561
|
+
The dataframe with properly formated gate results
|
|
562
|
+
df_o: Pandas DataFrame
|
|
563
|
+
The dataframe with properly formated non-gate results like SPAM error measures or fit quality.
|
|
564
|
+
identifier: BenchmarkObservationIdentifier
|
|
565
|
+
An identifier object for the current GST run
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
observation_list: List[BenchmarkObservation]
|
|
569
|
+
List of observations converted from the pandas dataframes
|
|
570
|
+
"""
|
|
571
|
+
observation_list: list[BenchmarkObservation] = []
|
|
572
|
+
err = dataset.attrs["bootstrap_samples"] > 0
|
|
573
|
+
for idx, gate_label in enumerate(dataset.attrs["gate_labels"].values()):
|
|
574
|
+
observation_list.extend(
|
|
575
|
+
[
|
|
576
|
+
BenchmarkObservation(
|
|
577
|
+
name=f"{name} - {gate_label}",
|
|
578
|
+
identifier=identifier,
|
|
579
|
+
value=result_str_to_floats(df_g[name].iloc[idx], err)[0],
|
|
580
|
+
uncertainty=result_str_to_floats(df_g[name].iloc[idx], err)[1],
|
|
581
|
+
)
|
|
582
|
+
for name in df_g.columns.tolist()
|
|
583
|
+
]
|
|
584
|
+
)
|
|
585
|
+
observation_list.extend(
|
|
586
|
+
[
|
|
587
|
+
BenchmarkObservation(
|
|
588
|
+
name=f"{name}",
|
|
589
|
+
identifier=identifier,
|
|
590
|
+
value=result_str_to_floats(df_o[name].iloc[0], err)[0],
|
|
591
|
+
uncertainty=result_str_to_floats(df_o[name].iloc[0], err)[1],
|
|
592
|
+
)
|
|
593
|
+
for name in df_o.columns.tolist()
|
|
594
|
+
]
|
|
595
|
+
)
|
|
596
|
+
return observation_list
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def dataset_counts_to_mgst_format(dataset: xr.Dataset, qubit_layout: List[int]) -> ndarray:
|
|
600
|
+
"""Turns the dictionary of outcomes obtained from qiskit backend
|
|
601
|
+
into the format which is used in mGST
|
|
602
|
+
|
|
603
|
+
Args:
|
|
604
|
+
dataset: xarray.Dataset
|
|
605
|
+
A dataset containing counts from the experiment and configurations
|
|
606
|
+
qubit_layout: List[int]
|
|
607
|
+
The list of qubits for the current GST experiment
|
|
608
|
+
|
|
609
|
+
Returns
|
|
610
|
+
-------
|
|
611
|
+
y : numpy array
|
|
612
|
+
2D array of measurement outcomes for sequences in J;
|
|
613
|
+
Each column contains the outcome probabilities for a fixed sequence
|
|
614
|
+
|
|
615
|
+
"""
|
|
616
|
+
num_qubits = len(qubit_layout)
|
|
617
|
+
num_povm = dataset.attrs["num_povm"]
|
|
618
|
+
y_list = []
|
|
619
|
+
for run_index in range(dataset.attrs["num_circuits"]):
|
|
620
|
+
# result = dataset[f"{qubit_layout}_counts_{run_index}"].to_dict()
|
|
621
|
+
result = dataset[f"{qubit_layout}_state_{run_index}"].data.tolist()
|
|
622
|
+
basis_dict = {entry: int("".join([entry[::-1][i] for i in range(num_qubits)][::-1]), 2) for entry in result}
|
|
623
|
+
# Sort by index:
|
|
624
|
+
basis_dict = dict(sorted(basis_dict.items(), key=lambda item: item[1]))
|
|
625
|
+
|
|
626
|
+
counts_normalized = (
|
|
627
|
+
dataset[f"{qubit_layout}_counts_{run_index}"] / dataset[f"{qubit_layout}_counts_{run_index}"].sum()
|
|
628
|
+
)
|
|
629
|
+
row = [counts_normalized.loc[key].data for key in basis_dict]
|
|
630
|
+
# row = [result[key] for key in basis_dict]
|
|
631
|
+
if len(row) < num_povm:
|
|
632
|
+
missing_entries = list(np.arange(num_povm))
|
|
633
|
+
for given_entry in basis_dict.values():
|
|
634
|
+
missing_entries.remove(given_entry)
|
|
635
|
+
for missing_entry in missing_entries:
|
|
636
|
+
row.insert(missing_entry, 0) # 0 measurement outcomes in not recorded entry
|
|
637
|
+
y_list.append(row)
|
|
638
|
+
y = np.array(y_list).T
|
|
639
|
+
return y
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def run_mGST(
|
|
643
|
+
dataset: xr.Dataset, y: ndarray
|
|
644
|
+
) -> tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]:
|
|
645
|
+
"""Wrapper function for mGST algorithm execution which prepares an initialization and sets the alg. parameters
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
dataset: xarray.Dataset
|
|
649
|
+
A dataset containing counts from the experiment and configurations
|
|
650
|
+
y: ndarray
|
|
651
|
+
The circuit outcome probabilities as a num_povm x num_circuits array
|
|
652
|
+
|
|
653
|
+
Returns:
|
|
654
|
+
K : ndarray
|
|
655
|
+
Kraus estimate array where each subarray along the first axis contains a set of Kraus operators.
|
|
656
|
+
The second axis enumerates Kraus operators for a gate specified by the first axis.
|
|
657
|
+
X : ndarray
|
|
658
|
+
Superoperator estimate array where reconstructed CPT superoperators in
|
|
659
|
+
standard basis are stacked along the first axis.
|
|
660
|
+
E : ndarray
|
|
661
|
+
Current POVM estimate
|
|
662
|
+
rho : ndarray
|
|
663
|
+
Current initial state estimate
|
|
664
|
+
K_target : ndarray
|
|
665
|
+
Target gate Kraus array where each subarray along the first axis contains a set of Kraus operators.
|
|
666
|
+
The second axis enumerates Kraus operators for a gate specified by the first axis.
|
|
667
|
+
X_target : ndarray
|
|
668
|
+
Target gate superoperator estimate array where reconstructed CPT superoperators in
|
|
669
|
+
standard basis are stacked along the first axis.
|
|
670
|
+
E_target : ndarray
|
|
671
|
+
Target POVM
|
|
672
|
+
rho_target : ndarray
|
|
673
|
+
Target initial state
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
K_target = qiskit_gate_to_operator(dataset.attrs["gate_set"])
|
|
677
|
+
X_target = np.einsum("ijkl,ijnm -> iknlm", K_target, K_target.conj()).reshape(
|
|
678
|
+
(dataset.attrs["num_gates"], dataset.attrs["pdim"] ** 2, dataset.attrs["pdim"] ** 2)
|
|
679
|
+
) # tensor of superoperators
|
|
680
|
+
|
|
681
|
+
rho_target = (
|
|
682
|
+
np.kron(additional_fns.basis(dataset.attrs["pdim"], 0).T.conj(), additional_fns.basis(dataset.attrs["pdim"], 0))
|
|
683
|
+
.reshape(-1)
|
|
684
|
+
.astype(np.complex128)
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
# Computational basis measurement:
|
|
688
|
+
E_target = np.array(
|
|
689
|
+
[
|
|
690
|
+
np.kron(
|
|
691
|
+
additional_fns.basis(dataset.attrs["pdim"], i).T.conj(), additional_fns.basis(dataset.attrs["pdim"], i)
|
|
692
|
+
).reshape(-1)
|
|
693
|
+
for i in range(dataset.attrs["pdim"])
|
|
694
|
+
]
|
|
695
|
+
).astype(np.complex128)
|
|
696
|
+
|
|
697
|
+
# Run mGST
|
|
698
|
+
if dataset.attrs["from_init"]:
|
|
699
|
+
K_init = additional_fns.perturbed_target_init(X_target, dataset.attrs["rank"])
|
|
700
|
+
init_params = [K_init, E_target, rho_target]
|
|
701
|
+
else:
|
|
702
|
+
init_params = None
|
|
703
|
+
|
|
704
|
+
K, X, E, rho, _ = algorithm.run_mGST(
|
|
705
|
+
y,
|
|
706
|
+
dataset.attrs["J"],
|
|
707
|
+
dataset.attrs["seq_len_list"][-1],
|
|
708
|
+
dataset.attrs["num_gates"],
|
|
709
|
+
dataset.attrs["pdim"] ** 2,
|
|
710
|
+
dataset.attrs["rank"],
|
|
711
|
+
dataset.attrs["num_povm"],
|
|
712
|
+
dataset.attrs["batch_size"],
|
|
713
|
+
dataset.attrs["shots"],
|
|
714
|
+
method=dataset.attrs["opt_method"],
|
|
715
|
+
max_inits=dataset.attrs["max_inits"],
|
|
716
|
+
max_iter=dataset.attrs["max_iterations"][0],
|
|
717
|
+
final_iter=dataset.attrs["max_iterations"][1],
|
|
718
|
+
threshold_multiplier=dataset.attrs["convergence_criteria"][0],
|
|
719
|
+
target_rel_prec=dataset.attrs["convergence_criteria"][1],
|
|
720
|
+
init=init_params,
|
|
721
|
+
testing=False,
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
return K, X, E, rho, K_target, X_target, E_target, rho_target
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def mgst_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
|
|
728
|
+
"""Analysis function for compressive GST
|
|
729
|
+
|
|
730
|
+
Args:
|
|
731
|
+
run: BenchmarkRunResult
|
|
732
|
+
A BenchmarkRunResult instance storing the dataset
|
|
733
|
+
Returns:
|
|
734
|
+
result: BenchmarkAnalysisResult
|
|
735
|
+
An BenchmarkAnalysisResult instance with the updated dataset, as well as plots and observations
|
|
736
|
+
"""
|
|
737
|
+
dataset = run.dataset
|
|
738
|
+
pdim = dataset.attrs["pdim"]
|
|
739
|
+
plots = {}
|
|
740
|
+
for i, qubit_layout in enumerate(dataset.attrs["qubit_layouts"]):
|
|
741
|
+
identifier = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
742
|
+
|
|
743
|
+
# Computing circuit outcome probabilities from counts
|
|
744
|
+
y = dataset_counts_to_mgst_format(dataset, qubit_layout)
|
|
745
|
+
|
|
746
|
+
# Main GST reconstruction
|
|
747
|
+
start_timer = perf_counter()
|
|
748
|
+
K, X, E, rho, K_target, X_target, E_target, rho_target = run_mGST(dataset, y)
|
|
749
|
+
main_gst_time = perf_counter() - start_timer
|
|
750
|
+
|
|
751
|
+
# Gauge optimization
|
|
752
|
+
start_timer = perf_counter()
|
|
753
|
+
target_mdl = compatibility.arrays_to_pygsti_model(X_target, E_target, rho_target, basis="std")
|
|
754
|
+
X_opt, E_opt, rho_opt = reporting.gauge_opt(X, E, rho, target_mdl, dataset.attrs[f"gauge_weights"])
|
|
755
|
+
gauge_optimization_time = perf_counter() - start_timer
|
|
756
|
+
|
|
757
|
+
# Quick report
|
|
758
|
+
df_g, _ = reporting.quick_report(
|
|
759
|
+
X_opt, E_opt, rho_opt, dataset.attrs["J"], y, target_mdl, dataset.attrs["gate_labels"]
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
# Gate set in the Pauli basis
|
|
763
|
+
X_opt_pp, _, _ = compatibility.std2pp(X_opt, E_opt, rho_opt)
|
|
764
|
+
X_target_pp, _, _ = compatibility.std2pp(X_target, E_target, rho_target)
|
|
765
|
+
|
|
766
|
+
# Saving
|
|
767
|
+
dataset.attrs["results_layout_" + identifier] = {
|
|
768
|
+
"raw_Kraus_operators": K,
|
|
769
|
+
"raw_gates": X,
|
|
770
|
+
"raw_POVM": E.reshape((dataset.attrs["num_povm"], pdim, pdim)),
|
|
771
|
+
"raw_state": rho.reshape((pdim, pdim)),
|
|
772
|
+
"gauge_opt_gates": X_opt,
|
|
773
|
+
"gauge_opt_gates_Pauli_basis": X_opt_pp,
|
|
774
|
+
"gauge_opt_POVM": E_opt.reshape((dataset.attrs["num_povm"], pdim, pdim)),
|
|
775
|
+
"gauge_opt_state": rho_opt.reshape((pdim, pdim)),
|
|
776
|
+
"main_mGST_time": main_gst_time,
|
|
777
|
+
"gauge_optimization_time": gauge_optimization_time,
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
### Bootstrap
|
|
781
|
+
if dataset.attrs["bootstrap_samples"] > 0:
|
|
782
|
+
bootstrap_results = bootstrap_errors(dataset, y, K, X, E, rho, target_mdl)
|
|
783
|
+
dataset.attrs["results_layout_" + identifier].update({"bootstrap_data": bootstrap_results})
|
|
784
|
+
|
|
785
|
+
_, df_o_full = reporting.report(
|
|
786
|
+
X_opt, E_opt, rho_opt, dataset.attrs["J"], y, target_mdl, dataset.attrs["gate_labels"]
|
|
787
|
+
)
|
|
788
|
+
df_o_final, fig_o = generate_non_gate_results(dataset, qubit_layout, df_o_full)
|
|
789
|
+
|
|
790
|
+
### Result table generation and full report
|
|
791
|
+
if dataset.attrs["rank"] == 1:
|
|
792
|
+
df_g_final, df_g_rotation, fig_g, fig_rotation = generate_unit_rank_gate_results(
|
|
793
|
+
dataset, qubit_layout, df_g, X_opt, K_target
|
|
794
|
+
)
|
|
795
|
+
dataset.attrs["results_layout_" + identifier].update({"hamiltonian_parameters": df_g_rotation.to_dict()})
|
|
796
|
+
plots[f"layout_{qubit_layout}_hamiltonian_parameters"] = fig_rotation
|
|
797
|
+
else:
|
|
798
|
+
df_g_final, df_g_evals, fig_g, fig_choi = generate_gate_results(
|
|
799
|
+
dataset, qubit_layout, df_g, X_opt, E_opt, rho_opt
|
|
800
|
+
)
|
|
801
|
+
dataset.attrs["results_layout_" + identifier].update({"choi_evals": df_g_evals.to_dict()})
|
|
802
|
+
plots[f"layout_{qubit_layout}_choi_eigenvalues"] = fig_choi
|
|
803
|
+
plots[f"layout_{qubit_layout}_gate_metrics"] = fig_g
|
|
804
|
+
plots[f"layout_{qubit_layout}_other_metrics"] = fig_o
|
|
805
|
+
|
|
806
|
+
observation_list = pandas_results_to_observations(
|
|
807
|
+
dataset, df_g_final, df_o_final, BenchmarkObservationIdentifier(qubit_layout)
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
dataset.attrs["results_layout_" + identifier].update(
|
|
811
|
+
{"full_metrics": {"Gates": df_g_final.to_dict(), "Outcomes and SPAM": df_o_final.to_dict()}}
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
### Process matrix plots
|
|
815
|
+
pauli_labels = generate_basis_labels(pdim, basis="Pauli")
|
|
816
|
+
std_labels = generate_basis_labels(pdim)
|
|
817
|
+
|
|
818
|
+
figures = figure_gen.generate_gate_err_pdf(
|
|
819
|
+
"",
|
|
820
|
+
X_opt_pp,
|
|
821
|
+
X_target_pp,
|
|
822
|
+
basis_labels=pauli_labels,
|
|
823
|
+
gate_labels=dataset.attrs["gate_labels"],
|
|
824
|
+
return_fig=True,
|
|
825
|
+
)
|
|
826
|
+
for i, figure in enumerate(figures):
|
|
827
|
+
plots[f"layout_{qubit_layout}_process_matrix_{i}"] = figure
|
|
828
|
+
|
|
829
|
+
plots[f"layout_{qubit_layout}_SPAM_matrices_real"] = figure_gen.generate_spam_err_std_pdf(
|
|
830
|
+
"",
|
|
831
|
+
E_opt.real,
|
|
832
|
+
rho_opt.real,
|
|
833
|
+
E_target.real,
|
|
834
|
+
rho_target.real,
|
|
835
|
+
basis_labels=std_labels,
|
|
836
|
+
title=f"Real part of state and measurement effects in the standard basis",
|
|
837
|
+
return_fig=True,
|
|
838
|
+
)
|
|
839
|
+
plots[f"layout_{qubit_layout}_SPAM_matrices_imag"] = figure_gen.generate_spam_err_std_pdf(
|
|
840
|
+
"",
|
|
841
|
+
E_opt.imag,
|
|
842
|
+
rho_opt.imag,
|
|
843
|
+
E_target.imag,
|
|
844
|
+
rho_target.imag,
|
|
845
|
+
basis_labels=std_labels,
|
|
846
|
+
title=f"Imaginary part of state and measurement effects in the standard basis",
|
|
847
|
+
return_fig=True,
|
|
848
|
+
)
|
|
849
|
+
plt.close("all")
|
|
850
|
+
|
|
851
|
+
return BenchmarkAnalysisResult(dataset=dataset, observations=observation_list, plots=plots)
|