iqm-benchmarks 2.44__py3-none-any.whl → 2.46__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.
- iqm/benchmarks/benchmark_definition.py +1 -0
- iqm/benchmarks/compressive_gst/compressive_gst.py +100 -46
- iqm/benchmarks/compressive_gst/gst_analysis.py +480 -381
- iqm/benchmarks/entanglement/ghz.py +2 -2
- iqm/benchmarks/optimization/qscore.py +7 -6
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/METADATA +2 -2
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/RECORD +17 -17
- mGST/additional_fns.py +35 -20
- mGST/algorithm.py +73 -57
- mGST/low_level_jit.py +122 -56
- mGST/optimization.py +12 -6
- mGST/qiskit_interface.py +64 -87
- mGST/reporting/figure_gen.py +390 -57
- mGST/reporting/reporting.py +209 -11
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/top_level.txt +0 -0
mGST/reporting/figure_gen.py
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
Generation of figures
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from typing import List, Union
|
|
6
|
+
|
|
5
7
|
from matplotlib import ticker
|
|
6
8
|
from matplotlib.colors import Normalize
|
|
9
|
+
from matplotlib.figure import Figure
|
|
7
10
|
import matplotlib.pyplot as plt
|
|
11
|
+
from matplotlib.transforms import Bbox
|
|
8
12
|
import numpy as np
|
|
9
13
|
import numpy.linalg as la
|
|
14
|
+
from pandas import DataFrame
|
|
15
|
+
import xarray as xr
|
|
16
|
+
|
|
17
|
+
from iqm.benchmarks.benchmark_definition import BenchmarkObservationIdentifier
|
|
18
|
+
from mGST.reporting.reporting import compute_matched_ideal_hamiltonian_params, generate_basis_labels
|
|
10
19
|
|
|
11
20
|
|
|
12
21
|
SMALL_SIZE = 8
|
|
@@ -21,7 +30,7 @@ plt.rc("ytick", labelsize=SMALL_SIZE) # fontsize of the tick labels
|
|
|
21
30
|
plt.rc("legend", fontsize=SMALL_SIZE) # legend fontsize
|
|
22
31
|
plt.rc("figure", titlesize=BIGGER_SIZE) # fontsize of the figure title
|
|
23
32
|
|
|
24
|
-
cmap = plt.colormaps.get_cmap("
|
|
33
|
+
cmap = plt.colormaps.get_cmap("coolwarm")
|
|
25
34
|
norm = Normalize(vmin=-1, vmax=1)
|
|
26
35
|
|
|
27
36
|
|
|
@@ -48,7 +57,7 @@ def set_size(w, h, ax=None):
|
|
|
48
57
|
ax.figure.set_size_inches(figw, figh)
|
|
49
58
|
|
|
50
59
|
|
|
51
|
-
def plot_objf(res_list,
|
|
60
|
+
def plot_objf(res_list, title, delta=None):
|
|
52
61
|
"""Plots the objective function over iterations in the algorithm
|
|
53
62
|
|
|
54
63
|
Parameters:
|
|
@@ -58,12 +67,15 @@ def plot_objf(res_list, delta, title):
|
|
|
58
67
|
|
|
59
68
|
Returns:
|
|
60
69
|
"""
|
|
61
|
-
|
|
70
|
+
if delta is not None:
|
|
71
|
+
plt.semilogy(res_list)
|
|
72
|
+
plt.axhline(delta, color="green", label="conv. threshold")
|
|
73
|
+
plt.legend()
|
|
74
|
+
else:
|
|
75
|
+
plt.semilogy(res_list)
|
|
62
76
|
plt.ylabel(f"Objective function")
|
|
63
77
|
plt.xlabel(f"Iterations")
|
|
64
|
-
plt.axhline(delta, color="green", label="conv. threshold")
|
|
65
78
|
plt.title(title)
|
|
66
|
-
plt.legend()
|
|
67
79
|
plt.show()
|
|
68
80
|
|
|
69
81
|
|
|
@@ -147,9 +159,7 @@ def generate_spam_err_pdf(filename, E, rho, E2, rho2, title=None, spam2_content=
|
|
|
147
159
|
plt.close()
|
|
148
160
|
|
|
149
161
|
|
|
150
|
-
def generate_spam_err_std_pdf(
|
|
151
|
-
filename, E, rho, E2, rho2, basis_labels=False, title=None, magnification=10, return_fig=False
|
|
152
|
-
):
|
|
162
|
+
def generate_spam_err_std_pdf(filename, E, rho, E2, rho2, basis_labels=False, title=None, return_fig=False):
|
|
153
163
|
"""Generate pdf plots of two sets of POVM + state side by side in matrix shape - standard basis
|
|
154
164
|
The input sets can be either POVM/state directly or a difference different SPAM parametrizations to
|
|
155
165
|
visualize errors.
|
|
@@ -170,8 +180,6 @@ def generate_spam_err_std_pdf(
|
|
|
170
180
|
The Figure title
|
|
171
181
|
basis_labels : list[str]
|
|
172
182
|
A list of labels for the basis elements. For the standard basis this could be ["00", "01",...]
|
|
173
|
-
magnification : float
|
|
174
|
-
A factor to be applied to magnify errors in the rightmost plot.
|
|
175
183
|
return_fig : bool
|
|
176
184
|
If set to True, a figure object is returned by the function, otherwise the plot is saved as <filename>
|
|
177
185
|
Returns
|
|
@@ -188,24 +196,31 @@ def generate_spam_err_std_pdf(
|
|
|
188
196
|
if i == 0:
|
|
189
197
|
plot_matrices = [np.real(rho), np.real(rho2), np.real(rho - rho2)]
|
|
190
198
|
axes[i, 0].set_ylabel(f"rho", rotation=90, fontsize="large")
|
|
199
|
+
axes[i, 0].set_title(f"Estimate", fontsize="large")
|
|
200
|
+
axes[i, 1].set_title(f"Reference", fontsize="large")
|
|
201
|
+
axes[i, 2].set_title(f"Deviation", fontsize="large")
|
|
191
202
|
else:
|
|
192
|
-
plot_matrices = [np.real(E[i - 1]), np.real(E2[i - 1]), np.real(E[i - 1] - E2[i - 1])
|
|
203
|
+
plot_matrices = [np.real(E[i - 1]), np.real(E2[i - 1]), np.real(E[i - 1] - E2[i - 1])]
|
|
193
204
|
axes[i, 0].set_ylabel(f"E_%i" % (i - 1), rotation=90, fontsize="large")
|
|
194
205
|
|
|
195
206
|
for j in range(3):
|
|
196
207
|
ax = axes[i, j]
|
|
197
208
|
ax.patch.set_facecolor("whitesmoke")
|
|
198
209
|
ax.set_aspect("equal")
|
|
210
|
+
max_entry = np.abs(plot_matrices[j]).max()
|
|
211
|
+
max_weight = 2 ** np.ceil(np.log2(max_entry, where=max_entry > 0))
|
|
199
212
|
for (x, y), w in np.ndenumerate(plot_matrices[j].reshape(pdim, pdim)):
|
|
200
|
-
|
|
213
|
+
if j == 2:
|
|
214
|
+
size = np.sqrt(np.abs(w) / max_weight)
|
|
215
|
+
else:
|
|
216
|
+
size = np.sqrt(np.abs(w))
|
|
201
217
|
rect = plt.Rectangle(
|
|
202
218
|
[x + (1 - size) / 2, y + (1 - size) / 2],
|
|
203
219
|
size,
|
|
204
220
|
size,
|
|
205
|
-
facecolor=
|
|
206
|
-
edgecolor=
|
|
221
|
+
facecolor="#d62728" if w < 0 else "#1f77b4",
|
|
222
|
+
edgecolor="#d62728" if w < 0 else "#1f77b4",
|
|
207
223
|
)
|
|
208
|
-
# print(cmap(size))
|
|
209
224
|
ax.add_patch(rect)
|
|
210
225
|
ax.invert_yaxis()
|
|
211
226
|
ax.set_xticks(np.arange(pdim + 1), labels=[])
|
|
@@ -222,15 +237,14 @@ def generate_spam_err_std_pdf(
|
|
|
222
237
|
ax.grid(visible="True", alpha=0.4)
|
|
223
238
|
ax.set_xticks(np.arange(pdim) + 0.5, minor=True, labels=basis_labels, rotation=45, fontsize=6)
|
|
224
239
|
ax.set_yticks(np.arange(pdim) + 0.5, minor=True, labels=basis_labels, fontsize=6)
|
|
225
|
-
|
|
226
|
-
fig.suptitle(title)
|
|
240
|
+
fig.suptitle(title)
|
|
227
241
|
|
|
228
|
-
if dim
|
|
229
|
-
fig.
|
|
230
|
-
|
|
242
|
+
if dim >= 16:
|
|
243
|
+
fig.subplots_adjust(left=0, right=1, top=0.9, bottom=0.05, wspace=0.2, hspace=0.2)
|
|
244
|
+
set_size(np.sqrt(dim), 2 * np.sqrt(dim))
|
|
231
245
|
else:
|
|
232
|
-
fig.
|
|
233
|
-
|
|
246
|
+
fig.subplots_adjust(left=0, right=1, top=0.76, bottom=0.05, wspace=-0.8, hspace=0.4)
|
|
247
|
+
set_size(3 * np.sqrt(dim), 1.2 * np.sqrt(dim))
|
|
234
248
|
|
|
235
249
|
if return_fig:
|
|
236
250
|
return fig
|
|
@@ -240,11 +254,10 @@ def generate_spam_err_std_pdf(
|
|
|
240
254
|
return None
|
|
241
255
|
|
|
242
256
|
|
|
243
|
-
def generate_gate_err_pdf(
|
|
244
|
-
filename, gates1, gates2, basis_labels=False, gate_labels=False, magnification=5, return_fig=False
|
|
245
|
-
):
|
|
257
|
+
def generate_gate_err_pdf(filename, gates1, gates2, basis_labels=False, gate_labels=False, return_fig=False):
|
|
246
258
|
"""Main routine to generate plots of reconstructed gates, ideal gates and the noise channels
|
|
247
|
-
of the reconstructed gates.
|
|
259
|
+
of the reconstructed gates. The matrices are shown as Hinton diagrams, where the size of each square represents
|
|
260
|
+
the magnitude of the matrix element and the color represents its sign as well as the magnitude.
|
|
248
261
|
The basis is arbitrary but using gates in the Pauli basis is recommended.
|
|
249
262
|
|
|
250
263
|
Parameters
|
|
@@ -260,75 +273,395 @@ def generate_gate_err_pdf(
|
|
|
260
273
|
and for the Pauli basis ["I", "X", "Y", "Z"] or the multi-qubit version.
|
|
261
274
|
gate_labels : list[str]
|
|
262
275
|
A list of names for the gates
|
|
263
|
-
magnification : float
|
|
264
|
-
A factor to be applied to magnify errors in the rightmost plot.
|
|
265
276
|
return_fig : bool
|
|
266
277
|
If set to True, a figure object is returned by the function, otherwise the plots are saved as <filename>
|
|
267
278
|
"""
|
|
268
279
|
d = gates1.shape[0]
|
|
269
280
|
dim = gates1[0].shape[0]
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if not gate_labels:
|
|
273
|
-
gate_labels = [f"G%i" % k for k in range(d)]
|
|
281
|
+
basis_labels = basis_labels or np.arange(dim)
|
|
282
|
+
gate_labels = gate_labels or [f"G%i" % k for k in range(d)]
|
|
274
283
|
plot3_title = r"id - G U^{-1}"
|
|
275
284
|
|
|
276
285
|
figures = []
|
|
277
286
|
for i in range(d):
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
287
|
+
# Determine layout based on dimension
|
|
288
|
+
is_large_dim = dim > 16
|
|
289
|
+
layout_params = {
|
|
290
|
+
"ncols": 1 if is_large_dim else 3,
|
|
291
|
+
"nrows": 3 if is_large_dim else 1,
|
|
292
|
+
"gridspec_kw": {"height_ratios": [1, 1, 1]} if is_large_dim else {"width_ratios": [1, 1, 1]},
|
|
293
|
+
"sharex": True,
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
fig, axes = plt.subplots(**layout_params)
|
|
283
297
|
plot_matrices = [
|
|
284
298
|
np.real(gates1[i]),
|
|
285
299
|
np.real(gates2[i]),
|
|
286
|
-
|
|
300
|
+
np.eye(dim) - np.real(gates1[i] @ la.inv(gates2[i])),
|
|
287
301
|
]
|
|
288
302
|
|
|
289
303
|
for j in range(3):
|
|
290
304
|
ax = axes[j]
|
|
291
305
|
ax.patch.set_facecolor("whitesmoke")
|
|
292
306
|
ax.set_aspect("equal")
|
|
307
|
+
max_weight = 2 ** np.ceil(np.log2(np.abs(plot_matrices[j]).max()))
|
|
308
|
+
|
|
309
|
+
# Plot matrix elements as rectangles, using normalization for the error plot (j==2)
|
|
293
310
|
for (x, y), w in np.ndenumerate(plot_matrices[j].T):
|
|
294
|
-
size = np.sqrt(np.abs(w))
|
|
311
|
+
size = np.sqrt(np.abs(w) / max_weight) if j == 2 else np.sqrt(np.abs(w))
|
|
312
|
+
color = "#d62728" if w < 0 else "#1f77b4"
|
|
313
|
+
|
|
295
314
|
rect = plt.Rectangle(
|
|
296
315
|
[x + (1 - size) / 2, y + (1 - size) / 2],
|
|
297
316
|
size,
|
|
298
317
|
size,
|
|
299
|
-
facecolor=
|
|
300
|
-
edgecolor=
|
|
318
|
+
facecolor=color,
|
|
319
|
+
edgecolor=color,
|
|
301
320
|
)
|
|
302
321
|
ax.add_patch(rect)
|
|
322
|
+
|
|
303
323
|
ax.invert_yaxis()
|
|
304
324
|
ax.set_xticks(np.arange(dim + 1), labels=[])
|
|
305
325
|
ax.set_yticks(np.arange(dim + 1), labels=[])
|
|
306
326
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
327
|
+
grid_params = {"visible": "True", "alpha": 0.4}
|
|
328
|
+
if is_large_dim:
|
|
329
|
+
grid_params["lw"] = 0.1
|
|
330
|
+
x_tick_params = {"fontsize": 2, "rotation": 45}
|
|
331
|
+
y_tick_params = {"fontsize": 2}
|
|
310
332
|
else:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
333
|
+
x_tick_params = {"rotation": 45}
|
|
334
|
+
y_tick_params = {}
|
|
335
|
+
|
|
336
|
+
ax.grid(**grid_params)
|
|
337
|
+
ax.set_xticks(np.arange(dim) + 0.5, minor=True, labels=basis_labels, **x_tick_params)
|
|
338
|
+
ax.set_yticks(np.arange(dim) + 0.5, minor=True, labels=basis_labels, **y_tick_params)
|
|
314
339
|
ax.tick_params(which="major", length=0) # Turn dummy ticks invisible
|
|
315
340
|
ax.tick_params(which="minor", top=True, labeltop=True, bottom=False, labelbottom=False, length=0)
|
|
316
341
|
|
|
317
342
|
axes[0].set_title(r"G (estimate)", fontsize="large")
|
|
318
343
|
axes[0].set_ylabel(gate_labels[i], rotation=90, fontsize="large")
|
|
319
344
|
axes[1].set_title(r"U (ideal gate)", fontsize="large")
|
|
320
|
-
axes[2].set_title(plot3_title, fontsize="large")
|
|
321
|
-
fig.suptitle(f"Process matrices in the Pauli basis
|
|
345
|
+
axes[2].set_title(plot3_title + "\n(renormalized)", fontsize="large")
|
|
346
|
+
fig.suptitle(f"Process matrices in the Pauli basis\n(red:<0; blue:>0)")
|
|
347
|
+
|
|
348
|
+
# Configure layout based on dimension size - reduce top margin
|
|
349
|
+
rect = [0, 0, 1, 0.85 if dim < 5 else 0.95]
|
|
350
|
+
fig.tight_layout(rect=rect)
|
|
351
|
+
|
|
352
|
+
width = 0.5 * np.sqrt(dim) if is_large_dim else 2 * np.sqrt(dim)
|
|
353
|
+
height = 1.3 * np.sqrt(dim) if is_large_dim else 0.8 * np.sqrt(dim)
|
|
354
|
+
set_size(width, height)
|
|
322
355
|
|
|
323
|
-
if dim > 16:
|
|
324
|
-
fig.colorbar(plt.cm.ScalarMappable(norm=norm, cmap=cmap), ax=axes.tolist(), pad=0, shrink=0.6)
|
|
325
|
-
fig.subplots_adjust(left=0.1, right=0.76, top=0.85, bottom=0.03)
|
|
326
|
-
set_size(0.5 * np.sqrt(dim), 1.3 * np.sqrt(dim))
|
|
327
|
-
else:
|
|
328
|
-
fig.colorbar(plt.cm.ScalarMappable(norm=norm, cmap=cmap), ax=axes.tolist(), pad=0)
|
|
329
|
-
fig.subplots_adjust(left=0.1, right=0.76, top=0.85, bottom=0.03, hspace=0.2)
|
|
330
|
-
set_size(2 * np.sqrt(dim), 0.8 * np.sqrt(dim))
|
|
331
356
|
figures.append(fig)
|
|
332
357
|
if not return_fig:
|
|
333
358
|
plt.savefig(filename + f"G%i.pdf" % i, dpi=150, transparent=True, bbox_inches="tight")
|
|
359
|
+
|
|
334
360
|
return figures
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def plot_largest_errors(
|
|
364
|
+
param_delta,
|
|
365
|
+
param_labels,
|
|
366
|
+
n_errs,
|
|
367
|
+
threshold,
|
|
368
|
+
gate_label,
|
|
369
|
+
has_uncertainties=False,
|
|
370
|
+
yerr_low=None,
|
|
371
|
+
yerr_high=None,
|
|
372
|
+
param_delta_high=None,
|
|
373
|
+
param_delta_low=None,
|
|
374
|
+
):
|
|
375
|
+
"""Generate a bar plot showing the largest coherent errors.
|
|
376
|
+
|
|
377
|
+
Parameters
|
|
378
|
+
----------
|
|
379
|
+
param_delta : numpy.ndarray
|
|
380
|
+
Difference between measured and ideal parameters
|
|
381
|
+
param_labels : list
|
|
382
|
+
Labels for the Pauli basis elements
|
|
383
|
+
n_errs : int
|
|
384
|
+
Number of largest errors to display
|
|
385
|
+
threshold : float
|
|
386
|
+
Minimum threshold for errors to display
|
|
387
|
+
gate_label : str
|
|
388
|
+
Label of the gate being visualized
|
|
389
|
+
has_uncertainties : bool, optional
|
|
390
|
+
Whether uncertainty data is available
|
|
391
|
+
yerr_low : numpy.ndarray, optional
|
|
392
|
+
Lower error bounds
|
|
393
|
+
yerr_high : numpy.ndarray, optional
|
|
394
|
+
Upper error bounds
|
|
395
|
+
param_delta_high : numpy.ndarray, optional
|
|
396
|
+
Upper bound differences
|
|
397
|
+
param_delta_low : numpy.ndarray, optional
|
|
398
|
+
Lower bound differences
|
|
399
|
+
|
|
400
|
+
Returns
|
|
401
|
+
-------
|
|
402
|
+
matplotlib.figure.Figure
|
|
403
|
+
The generated bar plot figure
|
|
404
|
+
"""
|
|
405
|
+
# Create figure and axis
|
|
406
|
+
fig_bar, ax = plt.subplots(figsize=(6, 4))
|
|
407
|
+
|
|
408
|
+
# Sort indices by absolute magnitude
|
|
409
|
+
sorting_indices = np.argsort(np.abs(param_delta))[::-1]
|
|
410
|
+
param_delta_sorted = param_delta[sorting_indices]
|
|
411
|
+
|
|
412
|
+
# Apply a threshold if provided to show more than n_errs errors
|
|
413
|
+
if threshold is not None:
|
|
414
|
+
mask = np.abs(param_delta_sorted) >= threshold
|
|
415
|
+
n_errs = max(n_errs, np.sum(mask))
|
|
416
|
+
|
|
417
|
+
# Truncate to determined number of errors
|
|
418
|
+
param_delta_sorted = param_delta_sorted[:n_errs]
|
|
419
|
+
sorting_indices = sorting_indices[:n_errs]
|
|
420
|
+
|
|
421
|
+
# Create bars
|
|
422
|
+
bars = ax.bar(
|
|
423
|
+
range(n_errs),
|
|
424
|
+
param_delta_sorted,
|
|
425
|
+
color="#1f77b4",
|
|
426
|
+
alpha=0.7,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Add error bars if available
|
|
430
|
+
if has_uncertainties:
|
|
431
|
+
error_low = yerr_low[sorting_indices]
|
|
432
|
+
error_high = yerr_high[sorting_indices]
|
|
433
|
+
error_positions = np.arange(n_errs)
|
|
434
|
+
|
|
435
|
+
ax.errorbar(
|
|
436
|
+
error_positions,
|
|
437
|
+
param_delta_sorted,
|
|
438
|
+
yerr=[error_low, error_high],
|
|
439
|
+
fmt="none",
|
|
440
|
+
ecolor=[0.2, 0.2, 0.2, 0.5],
|
|
441
|
+
capsize=3,
|
|
442
|
+
)
|
|
443
|
+
error_extend = np.max([np.max(np.abs(param_delta_high)), np.max(np.abs(param_delta_low))])
|
|
444
|
+
ax.set_ylim(-error_extend * 1.1, error_extend * 1.1)
|
|
445
|
+
else:
|
|
446
|
+
param_range = np.max(param_delta_sorted) - np.min(param_delta_sorted)
|
|
447
|
+
ax.set_ylim(np.min(param_delta_sorted) - param_range / 10, np.max(param_delta_sorted) + param_range / 10)
|
|
448
|
+
|
|
449
|
+
# Configure axis labels and title
|
|
450
|
+
ax.axhline(0, color="black", linewidth=0.8)
|
|
451
|
+
ax.set_xticks(range(n_errs))
|
|
452
|
+
ax.set_xticklabels(np.array(list(param_labels))[sorting_indices])
|
|
453
|
+
ax.set_xlabel("Pauli labels")
|
|
454
|
+
ax.set_ylabel("Deviation from target")
|
|
455
|
+
ax.set_title(f"Largest coherent errors for {gate_label}", fontsize=10)
|
|
456
|
+
|
|
457
|
+
# Add values on top of each bar
|
|
458
|
+
for i, bar_ in enumerate(bars):
|
|
459
|
+
value = param_delta[sorting_indices[i]]
|
|
460
|
+
height = bar_.get_height()
|
|
461
|
+
ax.annotate(
|
|
462
|
+
f"{value:.2e}",
|
|
463
|
+
xy=(bar_.get_x() + bar_.get_width() / 2, height),
|
|
464
|
+
xytext=(0, 2) if height > 0 else (0, -11), # vertical offset above or below the bar
|
|
465
|
+
textcoords="offset points",
|
|
466
|
+
ha="center",
|
|
467
|
+
va="bottom",
|
|
468
|
+
fontsize=9,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
return fig_bar
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def generate_hamiltonian_visualizations(
|
|
475
|
+
dataset: xr.Dataset, n_errs: int = 4, threshold: float = 0.01
|
|
476
|
+
) -> dict[str, Figure]:
|
|
477
|
+
"""
|
|
478
|
+
Plots the coherent errors as the difference of the entries of the Hamiltonian in the Pauli basis to its ideal values.
|
|
479
|
+
1. Matrix plots showing all parameters with color coding
|
|
480
|
+
2. Bar plots showing the largest coherent errors
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
dataset (xarray.Dataset): A dataset containing counts from the experiment, results, and configurations.
|
|
484
|
+
n_errs (int): Number of largest errors to plot in bar charts. Default is 4.
|
|
485
|
+
threshold (float, optional): Minimum threshold for errors to display in bar charts. The threshold can lead to
|
|
486
|
+
more than n_errs errors being displayed.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
Tuple[List[List[matplotlib.figure.Figure]], List[List[matplotlib.figure.Figure]]]:
|
|
490
|
+
A tuple containing two nested lists of figures for each qubit layout and gate:
|
|
491
|
+
- Matrix plots
|
|
492
|
+
- Bar plots
|
|
493
|
+
"""
|
|
494
|
+
# Get the Hamiltonian parameters and their ideal values
|
|
495
|
+
hamiltonian_params, hamiltonian_params_ideal = compute_matched_ideal_hamiltonian_params(dataset)
|
|
496
|
+
param_labels = generate_basis_labels(dataset.attrs["pdim"], basis="Pauli")
|
|
497
|
+
gate_labels = list(dataset.attrs["gate_labels"].values())
|
|
498
|
+
|
|
499
|
+
# Collect upper and lower end of the confidence intervals
|
|
500
|
+
qubit_layouts = dataset.attrs["qubit_layouts"]
|
|
501
|
+
has_uncertainties = all(
|
|
502
|
+
dataset.attrs[f"results_layout_{BenchmarkObservationIdentifier(layout).string_identifier}"][
|
|
503
|
+
"hamiltonian_params"
|
|
504
|
+
].get("uncertainties")
|
|
505
|
+
is not None
|
|
506
|
+
for layout in qubit_layouts
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if has_uncertainties:
|
|
510
|
+
hamiltonian_params_low = np.array(
|
|
511
|
+
[
|
|
512
|
+
dataset.attrs[f"results_layout_{BenchmarkObservationIdentifier(layout).string_identifier}"][
|
|
513
|
+
"hamiltonian_params"
|
|
514
|
+
]["uncertainties"][0]
|
|
515
|
+
for layout in qubit_layouts
|
|
516
|
+
]
|
|
517
|
+
)
|
|
518
|
+
hamiltonian_params_high = np.array(
|
|
519
|
+
[
|
|
520
|
+
dataset.attrs[f"results_layout_{BenchmarkObservationIdentifier(layout).string_identifier}"][
|
|
521
|
+
"hamiltonian_params"
|
|
522
|
+
]["uncertainties"][1]
|
|
523
|
+
for layout in qubit_layouts
|
|
524
|
+
]
|
|
525
|
+
)
|
|
526
|
+
else:
|
|
527
|
+
# Create dummy arrays if no uncertainties are available
|
|
528
|
+
hamiltonian_params_low = hamiltonian_params.copy()
|
|
529
|
+
hamiltonian_params_high = hamiltonian_params.copy()
|
|
530
|
+
|
|
531
|
+
plots = {}
|
|
532
|
+
|
|
533
|
+
for l_idx, layout in enumerate(qubit_layouts):
|
|
534
|
+
# Iterate through each layout's parameters
|
|
535
|
+
for params, params_ideal, params_low, params_high, gate_label in zip(
|
|
536
|
+
hamiltonian_params[l_idx],
|
|
537
|
+
hamiltonian_params_ideal[l_idx],
|
|
538
|
+
hamiltonian_params_low[l_idx],
|
|
539
|
+
hamiltonian_params_high[l_idx],
|
|
540
|
+
gate_labels[l_idx].values(),
|
|
541
|
+
):
|
|
542
|
+
|
|
543
|
+
# Generate figures for each gate in the layout
|
|
544
|
+
param_delta = params - params_ideal
|
|
545
|
+
param_delta_low = params_low - params_ideal if has_uncertainties else param_delta
|
|
546
|
+
param_delta_high = params_high - params_ideal if has_uncertainties else param_delta
|
|
547
|
+
|
|
548
|
+
# Get uncertainties as difference vectors for error bars
|
|
549
|
+
yerr_low = np.abs(param_delta - param_delta_low) if has_uncertainties else None
|
|
550
|
+
yerr_high = np.abs(param_delta_high - param_delta) if has_uncertainties else None
|
|
551
|
+
|
|
552
|
+
params_reshaped = params.reshape(-1, 1)
|
|
553
|
+
params_low_reshaped = params_low.reshape(-1, 1)
|
|
554
|
+
params_high_reshaped = params_high.reshape(-1, 1)
|
|
555
|
+
shape = params_reshaped.shape
|
|
556
|
+
max_size = 16
|
|
557
|
+
num_splits = int(np.ceil(shape[0] / max_size))
|
|
558
|
+
|
|
559
|
+
# Split param_vector into a smaller array to plot
|
|
560
|
+
param_splits = np.array_split(params_reshaped, num_splits, axis=0)
|
|
561
|
+
param_splits_low = np.array_split(params_low_reshaped, num_splits, axis=0)
|
|
562
|
+
param_splits_high = np.array_split(params_high_reshaped, num_splits, axis=0)
|
|
563
|
+
|
|
564
|
+
fig_matrix, axes = plt.subplots(len(param_splits), 1, figsize=(10, len(param_splits)))
|
|
565
|
+
if len(param_splits) == 1:
|
|
566
|
+
axes = [axes]
|
|
567
|
+
|
|
568
|
+
for idx, (param_split, param_split_low, param_split_high, ax) in enumerate(
|
|
569
|
+
zip(param_splits, param_splits_low, param_splits_high, axes)
|
|
570
|
+
):
|
|
571
|
+
split_size = param_split.shape[0]
|
|
572
|
+
im = ax.matshow(param_split.T, cmap="coolwarm", vmin=-0.05, vmax=0.05)
|
|
573
|
+
ax.set_yticks(np.arange(1), labels=[], rotation=0)
|
|
574
|
+
ax.set_xticks(
|
|
575
|
+
np.arange(split_size), labels=list(param_labels)[idx * split_size : (idx + 1) * split_size]
|
|
576
|
+
)
|
|
577
|
+
for ind_combined, (param_value, param_split_high, param_split_low) in enumerate(
|
|
578
|
+
zip(param_split.reshape(-1), param_split_high.reshape(-1), param_split_low.reshape(-1))
|
|
579
|
+
):
|
|
580
|
+
i, j = divmod(ind_combined, param_split.shape[1])
|
|
581
|
+
if has_uncertainties:
|
|
582
|
+
|
|
583
|
+
# Display upper bound above the parameter value
|
|
584
|
+
ax.text(
|
|
585
|
+
i, j - 0.2, f"{param_split_high:.1e}", va="center", ha="center", color="black", fontsize=6
|
|
586
|
+
)
|
|
587
|
+
# Display parameter value in the middle
|
|
588
|
+
ax.text(i, j, f"{param_value:.1e}", va="center", ha="center", color="black", fontweight="bold", fontsize=6)
|
|
589
|
+
# Display lower bound below the parameter value
|
|
590
|
+
ax.text(
|
|
591
|
+
i, j + 0.2, f"{param_split_low:.1e}", va="center", ha="center", color="black", fontsize=6
|
|
592
|
+
)
|
|
593
|
+
else:
|
|
594
|
+
# Just display the parameter value if no uncertainties
|
|
595
|
+
ax.text(i, j, f"{param_value:.1e}", va="center", ha="center", color="black", fontsize=6)
|
|
596
|
+
|
|
597
|
+
plt.title(f"Hamiltonian parameters for {gate_label}", fontsize=10)
|
|
598
|
+
|
|
599
|
+
fig_matrix.colorbar(im, ax=axes, fraction=0.005, pad=0.04)
|
|
600
|
+
plots.update({f"layout_{layout}_{gate_label}_Hamiltonian": fig_matrix})
|
|
601
|
+
plt.close(fig_matrix)
|
|
602
|
+
|
|
603
|
+
fig_bar = plot_largest_errors(
|
|
604
|
+
param_delta,
|
|
605
|
+
param_labels,
|
|
606
|
+
n_errs,
|
|
607
|
+
threshold,
|
|
608
|
+
gate_label,
|
|
609
|
+
has_uncertainties=has_uncertainties,
|
|
610
|
+
yerr_low=yerr_low,
|
|
611
|
+
yerr_high=yerr_high,
|
|
612
|
+
param_delta_high=param_delta_high,
|
|
613
|
+
param_delta_low=param_delta_low,
|
|
614
|
+
)
|
|
615
|
+
plots.update({f"layout_{layout}_{gate_label}_largest_coherent_errs": fig_bar})
|
|
616
|
+
plt.close(fig_bar)
|
|
617
|
+
|
|
618
|
+
return plots
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def dataframe_to_figure(
|
|
622
|
+
df: DataFrame, row_labels: Union[List[str], None] = None, col_width: float = 2, fontsize: int = 12
|
|
623
|
+
) -> Figure:
|
|
624
|
+
"""Turns a pandas DataFrame into a figure
|
|
625
|
+
This is needed to conform with the standard file saving routine of QCVV.
|
|
626
|
+
|
|
627
|
+
Args:
|
|
628
|
+
df: Pandas DataFrame
|
|
629
|
+
A dataframe table containing GST results
|
|
630
|
+
row_labels: List[str]
|
|
631
|
+
The row labels for the dataframe
|
|
632
|
+
col_width: int
|
|
633
|
+
Used to control cell width in the table
|
|
634
|
+
fontsize: int
|
|
635
|
+
Font size of text/numbers in table cells
|
|
636
|
+
|
|
637
|
+
Returns:
|
|
638
|
+
figure: Matplotlib figure object
|
|
639
|
+
A figure representing the dataframe.
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
if row_labels is None:
|
|
643
|
+
row_labels = list(np.arange(df.shape[0]))
|
|
644
|
+
|
|
645
|
+
row_height = fontsize / 70 * 2
|
|
646
|
+
n_cols = df.shape[1]
|
|
647
|
+
n_rows = df.shape[0]
|
|
648
|
+
figsize = np.array([n_cols + 1, n_rows + 1]) * np.array([col_width, row_height])
|
|
649
|
+
|
|
650
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
651
|
+
|
|
652
|
+
fig.patch.set_visible(False)
|
|
653
|
+
ax.axis("off")
|
|
654
|
+
ax.axis("tight")
|
|
655
|
+
data_array = (df.to_numpy(dtype="str")).copy()
|
|
656
|
+
column_names = df.columns.tolist()
|
|
657
|
+
table = ax.table(
|
|
658
|
+
cellText=data_array,
|
|
659
|
+
colLabels=column_names,
|
|
660
|
+
rowLabels=row_labels,
|
|
661
|
+
cellLoc="center",
|
|
662
|
+
colColours=["#7FA1C3" for _ in range(n_cols)],
|
|
663
|
+
bbox=Bbox([[0, 0], [1, 1]]),
|
|
664
|
+
)
|
|
665
|
+
table.set_fontsize(fontsize)
|
|
666
|
+
table.set_figure(fig)
|
|
667
|
+
return fig
|