iqm-benchmarks 2.43__py3-none-any.whl → 2.45__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iqm-benchmarks might be problematic. Click here for more details.
- iqm/benchmarks/benchmark_definition.py +1 -0
- iqm/benchmarks/compressive_gst/compressive_gst.py +100 -46
- iqm/benchmarks/compressive_gst/gst_analysis.py +480 -381
- iqm/benchmarks/entanglement/ghz.py +5 -5
- iqm/benchmarks/entanglement/graph_states.py +21 -21
- iqm/benchmarks/optimization/qscore.py +2 -2
- iqm/benchmarks/quantum_volume/clops.py +2 -3
- iqm/benchmarks/randomized_benchmarking/eplg/eplg.py +23 -24
- iqm/benchmarks/utils.py +72 -8
- iqm/benchmarks/utils_plots.py +309 -65
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/METADATA +2 -2
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/RECORD +22 -22
- mGST/additional_fns.py +35 -20
- mGST/algorithm.py +73 -57
- mGST/low_level_jit.py +122 -56
- mGST/optimization.py +12 -6
- mGST/qiskit_interface.py +64 -87
- mGST/reporting/figure_gen.py +390 -57
- mGST/reporting/reporting.py +209 -11
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/top_level.txt +0 -0
mGST/reporting/reporting.py
CHANGED
|
@@ -4,7 +4,9 @@ Generation of error measures and result tables
|
|
|
4
4
|
|
|
5
5
|
from argparse import Namespace
|
|
6
6
|
import csv
|
|
7
|
+
from itertools import product
|
|
7
8
|
import os
|
|
9
|
+
from typing import List, Tuple, Union
|
|
8
10
|
|
|
9
11
|
import numpy as np
|
|
10
12
|
import numpy.linalg as la
|
|
@@ -17,10 +19,12 @@ from pygsti.tools import change_basis
|
|
|
17
19
|
from pygsti.tools.optools import compute_povm_map
|
|
18
20
|
from qiskit.quantum_info import SuperOp
|
|
19
21
|
from qiskit.quantum_info.operators.measures import diamond_norm
|
|
20
|
-
from scipy.linalg import logm, schur
|
|
22
|
+
from scipy.linalg import expm, logm, schur
|
|
21
23
|
from scipy.optimize import linear_sum_assignment, minimize
|
|
24
|
+
import xarray as xr
|
|
22
25
|
|
|
23
|
-
from
|
|
26
|
+
from iqm.benchmarks.benchmark_definition import BenchmarkObservationIdentifier
|
|
27
|
+
from mGST import additional_fns, algorithm, compatibility, low_level_jit, qiskit_interface
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
def min_spectral_distance(X1, X2):
|
|
@@ -135,6 +139,33 @@ def gauge_opt(X, E, rho, target_mdl, weights):
|
|
|
135
139
|
return compatibility.pygsti_model_to_arrays(gauge_optimized_mdl, basis="std")
|
|
136
140
|
|
|
137
141
|
|
|
142
|
+
def generate_basis_labels(pdim: int, basis: Union[str, None] = None) -> List[str]:
|
|
143
|
+
"""Generate a list of labels for the Pauli basis or the standard basis
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
pdim: int
|
|
147
|
+
Physical dimension
|
|
148
|
+
basis: str
|
|
149
|
+
Which basis the labels correspond to, currently default is standard basis and "Pauli" can be choose
|
|
150
|
+
for Pauli basis labels like "II", "IX", "XX", ...
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
labels: List[str]
|
|
154
|
+
A list of all string combinations for the given dimension and basis
|
|
155
|
+
"""
|
|
156
|
+
separator = ""
|
|
157
|
+
if basis == "Pauli":
|
|
158
|
+
pauli_labels_loc = ["I", "X", "Y", "Z"]
|
|
159
|
+
pauli_labels_rep = [pauli_labels_loc for _ in range(int(np.log2(pdim)))]
|
|
160
|
+
labels = [separator.join(map(str, x)) for x in product(*pauli_labels_rep)]
|
|
161
|
+
else:
|
|
162
|
+
std_labels_loc = ["0", "1"]
|
|
163
|
+
std_labels_rep = [std_labels_loc for _ in range(int(np.log2(pdim)))]
|
|
164
|
+
labels = [separator.join(map(str, x)) for x in product(*std_labels_rep)]
|
|
165
|
+
|
|
166
|
+
return labels
|
|
167
|
+
|
|
168
|
+
|
|
138
169
|
def report(X, E, rho, J, y, target_mdl, gate_labels):
|
|
139
170
|
"""Generation of pandas dataframes with gate and SPAM quality measures
|
|
140
171
|
The resutls can be converted to .tex tables or other formats to be used for GST reports
|
|
@@ -163,8 +194,6 @@ def report(X, E, rho, J, y, target_mdl, gate_labels):
|
|
|
163
194
|
DataFrame of gate quality measures
|
|
164
195
|
df_o : Pandas DataFrame
|
|
165
196
|
DataFrame of all other quality/error measures
|
|
166
|
-
s_g : Pandas DataFrame.style object
|
|
167
|
-
s_o : Pandas DataFrame.style object
|
|
168
197
|
"""
|
|
169
198
|
pdim = int(np.sqrt(rho.shape[0]))
|
|
170
199
|
X_t, E_t, rho_t = compatibility.pygsti_model_to_arrays(target_mdl, basis="std")
|
|
@@ -231,8 +260,6 @@ def quick_report(X, E, rho, J, y, target_mdl, gate_labels=None):
|
|
|
231
260
|
DataFrame of gate quality measures
|
|
232
261
|
df_o : Pandas DataFrame
|
|
233
262
|
DataFrame of all other quality/error measures
|
|
234
|
-
s_g : Pandas DataFrame.style object
|
|
235
|
-
s_o : Pandas DataFrame.style object
|
|
236
263
|
"""
|
|
237
264
|
pdim = int(np.sqrt(rho.shape[0]))
|
|
238
265
|
d = X.shape[0]
|
|
@@ -359,6 +386,155 @@ def compute_sparsest_Pauli_Hamiltonian(U_set):
|
|
|
359
386
|
return pauli_coeffs
|
|
360
387
|
|
|
361
388
|
|
|
389
|
+
def match_hamiltonian_phase(pauli_coeffs, pauli_coeffs_target):
|
|
390
|
+
"""
|
|
391
|
+
Matches the sign of the phase of a target gate Hamiltonian represented by Pauli coefficients to a measured Hamiltonian.
|
|
392
|
+
|
|
393
|
+
Parameters:
|
|
394
|
+
pauli_coeffs (numpy array): Pauli coefficients of the measured Hamiltonian.
|
|
395
|
+
pauli_coeffs_target (numpy array): Pauli coefficients of the target Hamiltonian.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
numpy array: Pauli coefficients of the target Hamiltonian with matched phase.
|
|
399
|
+
"""
|
|
400
|
+
dim = int(np.sqrt(len(pauli_coeffs)))
|
|
401
|
+
H_target = change_basis(pauli_coeffs_target, "pp", "std").reshape((dim, dim))
|
|
402
|
+
U_target = expm(-1j * H_target * np.pi * np.sqrt(dim) / 2)
|
|
403
|
+
U_target_alt = expm(1j * H_target * np.pi * np.sqrt(dim) / 2)
|
|
404
|
+
|
|
405
|
+
# Check if sign flip leads to same unitary
|
|
406
|
+
if np.linalg.norm(U_target - U_target_alt) < 1e-6:
|
|
407
|
+
# Check if sign flip leads to better match to target Hamiltonian in Pauli basis
|
|
408
|
+
if np.linalg.norm(pauli_coeffs_target - pauli_coeffs) > np.linalg.norm(pauli_coeffs_target + pauli_coeffs):
|
|
409
|
+
return -pauli_coeffs_target
|
|
410
|
+
|
|
411
|
+
return pauli_coeffs_target
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def generate_rotation_param_results(
|
|
415
|
+
dataset: xr.Dataset,
|
|
416
|
+
qubit_layout: List[int],
|
|
417
|
+
X_opt: np.ndarray,
|
|
418
|
+
K_target: np.ndarray,
|
|
419
|
+
X_array: np.ndarray = None,
|
|
420
|
+
E_array: np.ndarray = None,
|
|
421
|
+
rho_array: np.ndarray = None,
|
|
422
|
+
) -> Tuple[pd.DataFrame, dict]:
|
|
423
|
+
"""
|
|
424
|
+
Produces result tables and data for Kraus rank 1 estimates.
|
|
425
|
+
|
|
426
|
+
This includes parameters of the Hamiltonian generators in the Pauli basis for all gates.
|
|
427
|
+
If bootstrapping data is available, error bars will also be generated.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
dataset: xarray.Dataset
|
|
431
|
+
A dataset containing counts from the experiment and configurations
|
|
432
|
+
qubit_layout: List[int]
|
|
433
|
+
The list of qubits for the current GST experiment
|
|
434
|
+
X_opt: 3D numpy array
|
|
435
|
+
The gate set after gauge optimization
|
|
436
|
+
K_target: 4D numpy array
|
|
437
|
+
The Kraus operators of all target gates, used to compute distance measures.
|
|
438
|
+
X_array: ndarray, optional
|
|
439
|
+
Array of bootstrap gate estimates, used for error bars
|
|
440
|
+
E_array: ndarray, optional
|
|
441
|
+
Array of bootstrap POVM estimates, used for error bars
|
|
442
|
+
rho_array: ndarray, optional
|
|
443
|
+
Array of bootstrap state estimates, used for error bars
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
Tuple[DataFrame, dict]:
|
|
447
|
+
- df_g_rotation: Pandas DataFrame containing Hamiltonian (rotation) parameters with formatted values
|
|
448
|
+
- hamiltonian_params: Dictionary with raw values and uncertainty bounds if bootstrapping was used
|
|
449
|
+
"""
|
|
450
|
+
identifier = BenchmarkObservationIdentifier(qubit_layout).string_identifier
|
|
451
|
+
pauli_labels = generate_basis_labels(dataset.attrs["pdim"], basis="Pauli")
|
|
452
|
+
|
|
453
|
+
U_opt = phase_opt(X_opt, K_target)
|
|
454
|
+
pauli_coeffs = compute_sparsest_Pauli_Hamiltonian(U_opt)
|
|
455
|
+
|
|
456
|
+
bootstrap = X_array is not None and E_array is not None and rho_array is not None
|
|
457
|
+
|
|
458
|
+
if bootstrap:
|
|
459
|
+
bootstrap_pauli_coeffs = np.zeros((len(X_array), dataset.attrs["num_gates"], dataset.attrs["pdim"] ** 2))
|
|
460
|
+
for i, X_ in enumerate(X_array):
|
|
461
|
+
X_std, _, _ = compatibility.pp2std(X_, E_array[i], rho_array[i])
|
|
462
|
+
U_opt_ = phase_opt(X_std, K_target)
|
|
463
|
+
pauli_coeffs_ = compute_sparsest_Pauli_Hamiltonian(U_opt_)
|
|
464
|
+
bootstrap_pauli_coeffs[i, :, :] = pauli_coeffs_
|
|
465
|
+
pauli_coeffs_low, pauli_coeffs_high = np.nanpercentile(bootstrap_pauli_coeffs, [2.5, 97.5], axis=0)
|
|
466
|
+
|
|
467
|
+
# Pandas dataframe with formated confidence intervals
|
|
468
|
+
df_g_rotation = pd.DataFrame(
|
|
469
|
+
np.array(
|
|
470
|
+
[
|
|
471
|
+
[
|
|
472
|
+
number_to_str(
|
|
473
|
+
pauli_coeffs[i, j],
|
|
474
|
+
[pauli_coeffs_high[i, j], pauli_coeffs_low[i, j]] if bootstrap else None,
|
|
475
|
+
precision=5,
|
|
476
|
+
)
|
|
477
|
+
for i in range(dataset.attrs["num_gates"])
|
|
478
|
+
]
|
|
479
|
+
for j in range(dataset.attrs["pdim"] ** 2)
|
|
480
|
+
]
|
|
481
|
+
).T
|
|
482
|
+
)
|
|
483
|
+
df_g_rotation.columns = [f"h_%s" % label for label in pauli_labels]
|
|
484
|
+
df_g_rotation.rename(index=dataset.attrs["gate_labels"][identifier], inplace=True)
|
|
485
|
+
|
|
486
|
+
hamiltonian_params = {
|
|
487
|
+
"values": pauli_coeffs,
|
|
488
|
+
"uncertainties": (pauli_coeffs_low, pauli_coeffs_high) if bootstrap else None,
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return df_g_rotation, hamiltonian_params
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def compute_matched_ideal_hamiltonian_params(dataset: xr.Dataset) -> Tuple[np.ndarray, np.ndarray]:
|
|
495
|
+
"""
|
|
496
|
+
Computes the Hamiltonian parameters and matches the ideal Hamiltonian parameters to the measured ones (without changing the unitary it generates).
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
dataset (xarray.Dataset): A dataset containing counts from the experiment and configurations.
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
Tuple[numpy.ndarray, numpy.ndarray]: A tuple containing:
|
|
503
|
+
- hamiltonian_params: Hamiltonian parameters of the measured gates.
|
|
504
|
+
- hamiltonian_params_ideal_matched: Ideal Hamiltonian parameters matched to the measured ones.
|
|
505
|
+
"""
|
|
506
|
+
|
|
507
|
+
qubit_layouts = dataset.attrs["qubit_layouts"]
|
|
508
|
+
param_list_layouts = [
|
|
509
|
+
dataset.attrs[f"results_layout_{BenchmarkObservationIdentifier(layout).string_identifier}"][
|
|
510
|
+
"hamiltonian_params"
|
|
511
|
+
]["values"]
|
|
512
|
+
for layout in qubit_layouts
|
|
513
|
+
]
|
|
514
|
+
hamiltonian_params = np.array(param_list_layouts)
|
|
515
|
+
|
|
516
|
+
K_target = qiskit_interface.qiskit_gate_to_operator(dataset.attrs["gate_set"])
|
|
517
|
+
X_target = np.einsum("ijkl,ijnm -> iknlm", K_target, K_target.conj()).reshape(
|
|
518
|
+
(dataset.attrs["num_gates"], dataset.attrs["pdim"] ** 2, dataset.attrs["pdim"] ** 2)
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
param_list_layouts = []
|
|
522
|
+
for qubit_layout in qubit_layouts:
|
|
523
|
+
_, ideal_params = generate_rotation_param_results(dataset, qubit_layout, X_target, K_target)
|
|
524
|
+
ideal_params = ideal_params["values"]
|
|
525
|
+
param_list_layouts.append(ideal_params)
|
|
526
|
+
hamiltonian_params_ideal = np.array(param_list_layouts)
|
|
527
|
+
|
|
528
|
+
hamiltonian_params_ideal_matched = np.empty(hamiltonian_params_ideal.shape)
|
|
529
|
+
for i, _ in enumerate(qubit_layouts):
|
|
530
|
+
for j in range(hamiltonian_params_ideal.shape[1]):
|
|
531
|
+
hamiltonian_params_ideal_matched[i, j, :] = match_hamiltonian_phase(
|
|
532
|
+
hamiltonian_params[i, j, :], hamiltonian_params_ideal[i, j, :]
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
return hamiltonian_params, hamiltonian_params_ideal_matched
|
|
536
|
+
|
|
537
|
+
|
|
362
538
|
def phase_err(angle, U, U_t):
|
|
363
539
|
"""Computes norm between two input unitaries after a global phase is added to one of them
|
|
364
540
|
|
|
@@ -379,7 +555,7 @@ def phase_err(angle, U, U_t):
|
|
|
379
555
|
return la.norm(np.exp(1j * angle) * U - U_t)
|
|
380
556
|
|
|
381
557
|
|
|
382
|
-
def phase_opt(X, K_t):
|
|
558
|
+
def phase_opt(X: np.ndarray, K_t: np.ndarray):
|
|
383
559
|
"""Return rK = 1 gate set with global phase fitting matching to target gate set
|
|
384
560
|
|
|
385
561
|
Parameters
|
|
@@ -543,12 +719,12 @@ def bootstrap_errors(K, X, E, rho, mGST_args, bootstrap_samples, weights, gate_l
|
|
|
543
719
|
|
|
544
720
|
Parameters
|
|
545
721
|
----------
|
|
546
|
-
K
|
|
722
|
+
K: numpy array
|
|
547
723
|
Each subarray along the first axis contains a set of Kraus operators.
|
|
548
724
|
The second axis enumerates Kraus operators for a gate specified by the first axis.
|
|
549
|
-
X
|
|
725
|
+
X: 3D numpy array
|
|
550
726
|
Array where reconstructed CPT superoperators in standard basis are stacked along the first axis.
|
|
551
|
-
E
|
|
727
|
+
E: numpy array
|
|
552
728
|
Current POVM estimate
|
|
553
729
|
rho : numpy array
|
|
554
730
|
Current initial state estimate
|
|
@@ -611,7 +787,6 @@ def bootstrap_errors(K, X, E, rho, mGST_args, bootstrap_samples, weights, gate_l
|
|
|
611
787
|
threshold_multiplier=ns.threshold_multiplier,
|
|
612
788
|
target_rel_prec=ns.target_rel_prec,
|
|
613
789
|
init=[K, E, rho],
|
|
614
|
-
testing=False,
|
|
615
790
|
)
|
|
616
791
|
|
|
617
792
|
X_opt, E_opt, rho_opt = gauge_opt(X_, E_, rho_, target_mdl, weights)
|
|
@@ -711,3 +886,26 @@ def number_to_str(number, uncertainty=None, precision=3):
|
|
|
711
886
|
return f"{number:.{precision}f}"
|
|
712
887
|
|
|
713
888
|
return f"{number:.{precision}f} [{uncertainty[1]:.{precision}f},{uncertainty[0]:.{precision}f}]"
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
def result_str_to_floats(result_str: str, err: str) -> Tuple[float, float]:
|
|
892
|
+
"""Converts formated string results from mgst to float (value, uncertainty) pairs
|
|
893
|
+
|
|
894
|
+
Args:
|
|
895
|
+
result_str: str
|
|
896
|
+
The value of a result parameter formated as str
|
|
897
|
+
err: str
|
|
898
|
+
The error interval of the parameters
|
|
899
|
+
|
|
900
|
+
Returns:
|
|
901
|
+
value: float
|
|
902
|
+
The parameter value as float
|
|
903
|
+
uncertainty: float
|
|
904
|
+
A single uncertainty value
|
|
905
|
+
"""
|
|
906
|
+
if err:
|
|
907
|
+
value = float(result_str.split("[")[0])
|
|
908
|
+
rest = result_str.split("[")[1].split(",")
|
|
909
|
+
uncertainty = float(rest[1][:-1]) - float(rest[0])
|
|
910
|
+
return value, uncertainty
|
|
911
|
+
return float(result_str), np.NaN
|
|
File without changes
|
|
File without changes
|
|
File without changes
|