iqm-benchmarks 2.26__py3-none-any.whl → 2.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iqm-benchmarks might be problematic. Click here for more details.
- iqm/benchmarks/__init__.py +2 -0
- iqm/benchmarks/compressive_gst/gst_analysis.py +6 -2
- iqm/benchmarks/entanglement/__init__.py +2 -1
- iqm/benchmarks/entanglement/ghz.py +10 -10
- iqm/benchmarks/entanglement/graph_states.py +1348 -0
- iqm/benchmarks/optimization/qscore.py +1 -1
- iqm/benchmarks/quantum_volume/clops.py +1 -1
- iqm/benchmarks/quantum_volume/quantum_volume.py +3 -6
- iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl +0 -0
- iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl +0 -0
- iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py +1 -1
- iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py +33 -8
- iqm/benchmarks/utils.py +313 -236
- iqm/benchmarks/utils_plots.py +233 -0
- iqm/benchmarks/utils_shadows.py +228 -0
- {iqm_benchmarks-2.26.dist-info → iqm_benchmarks-2.28.dist-info}/METADATA +6 -4
- {iqm_benchmarks-2.26.dist-info → iqm_benchmarks-2.28.dist-info}/RECORD +21 -18
- {iqm_benchmarks-2.26.dist-info → iqm_benchmarks-2.28.dist-info}/WHEEL +1 -1
- mGST/algorithm.py +1 -1
- {iqm_benchmarks-2.26.dist-info → iqm_benchmarks-2.28.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.26.dist-info → iqm_benchmarks-2.28.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Copyright 2024 IQM Benchmarks developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Plotting and visualization utility functions
|
|
17
|
+
"""
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Dict, Literal, Optional, Tuple
|
|
20
|
+
|
|
21
|
+
import matplotlib.pyplot as plt
|
|
22
|
+
import networkx as nx
|
|
23
|
+
import numpy as np
|
|
24
|
+
from qiskit.transpiler import CouplingMap
|
|
25
|
+
from rustworkx import PyGraph, spring_layout, visualization # pylint: disable=no-name-in-module
|
|
26
|
+
|
|
27
|
+
from iqm.benchmarks.utils import extract_fidelities
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class GraphPositions:
|
|
32
|
+
"""A class to store and generate graph positions for different chip layouts.
|
|
33
|
+
|
|
34
|
+
This class contains predefined node positions for various quantum chip topologies and
|
|
35
|
+
provides methods to generate positions for different layout types.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
garnet_positions (Dict[int, Tuple[int, int]]): Mapping of node indices to (x,y) positions for Garnet chip.
|
|
39
|
+
deneb_positions (Dict[int, Tuple[int, int]]): Mapping of node indices to (x,y) positions for Deneb chip.
|
|
40
|
+
predefined_stations (Dict[str, Dict[int, Tuple[int, int]]]): Mapping of chip names to their position dictionaries.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
garnet_positions = {
|
|
44
|
+
0: (5.0, 7.0),
|
|
45
|
+
1: (6.0, 6.0),
|
|
46
|
+
2: (3.0, 7.0),
|
|
47
|
+
3: (4.0, 6.0),
|
|
48
|
+
4: (5.0, 5.0),
|
|
49
|
+
5: (6.0, 4.0),
|
|
50
|
+
6: (7.0, 3.0),
|
|
51
|
+
7: (2.0, 6.0),
|
|
52
|
+
8: (3.0, 5.0),
|
|
53
|
+
9: (4.0, 4.0),
|
|
54
|
+
10: (5.0, 3.0),
|
|
55
|
+
11: (6.0, 2.0),
|
|
56
|
+
12: (1.0, 5.0),
|
|
57
|
+
13: (2.0, 4.0),
|
|
58
|
+
14: (3.0, 3.0),
|
|
59
|
+
15: (4.0, 2.0),
|
|
60
|
+
16: (5.0, 1.0),
|
|
61
|
+
17: (1.0, 3.0),
|
|
62
|
+
18: (2.0, 2.0),
|
|
63
|
+
19: (3.0, 1.0),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
deneb_positions = {
|
|
67
|
+
0: (2.0, 2.0),
|
|
68
|
+
1: (1.0, 1.0),
|
|
69
|
+
3: (2.0, 1.0),
|
|
70
|
+
5: (3.0, 1.0),
|
|
71
|
+
2: (1.0, 3.0),
|
|
72
|
+
4: (2.0, 3.0),
|
|
73
|
+
6: (3.0, 3.0),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
predefined_stations = {"garnet": garnet_positions, "deneb": deneb_positions}
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def create_positions(
|
|
81
|
+
graph: PyGraph, topology: Optional[Literal["star", "crystal"]] = None
|
|
82
|
+
) -> Dict[int, Tuple[float, float]]:
|
|
83
|
+
"""Generate node positions for a given graph and topology.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
graph (PyGraph): The graph to generate positions for.
|
|
87
|
+
topology (Optional[Literal["star", "crystal"]]): The type of layout to generate. Must be either "star" or "crystal".
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Dict[int, Tuple[float, float]]: A dictionary mapping node indices to (x,y) coordinates.
|
|
91
|
+
"""
|
|
92
|
+
n_nodes = len(graph.node_indices())
|
|
93
|
+
|
|
94
|
+
if topology == "star":
|
|
95
|
+
# Place center node at (0,0)
|
|
96
|
+
pos = {0: (0.0, 0.0)}
|
|
97
|
+
|
|
98
|
+
if n_nodes > 1:
|
|
99
|
+
# Place other nodes in a circle around the center
|
|
100
|
+
angles = np.linspace(0, 2 * np.pi, n_nodes - 1, endpoint=False)
|
|
101
|
+
radius = 1.0
|
|
102
|
+
|
|
103
|
+
for i, angle in enumerate(angles, start=1):
|
|
104
|
+
x = radius * np.cos(angle)
|
|
105
|
+
y = radius * np.sin(angle)
|
|
106
|
+
pos[i] = (x, y)
|
|
107
|
+
|
|
108
|
+
# Crystal and other topologies
|
|
109
|
+
else:
|
|
110
|
+
# Fix first node position in bottom right
|
|
111
|
+
fixed_pos = {0: (1.0, 1.0)} # For more consistent layouts
|
|
112
|
+
|
|
113
|
+
# Get spring layout with one fixed position
|
|
114
|
+
pos = {
|
|
115
|
+
int(k): (float(v[0]), float(v[1]))
|
|
116
|
+
for k, v in spring_layout(graph, scale=2, pos=fixed_pos, num_iter=300, fixed={0}).items()
|
|
117
|
+
}
|
|
118
|
+
return pos
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def plot_layout_fidelity_graph(
|
|
122
|
+
cal_url: str, qubit_layouts: Optional[list[list[int]]] = None, station: Optional[str] = None
|
|
123
|
+
):
|
|
124
|
+
"""Plot a graph showing the quantum chip layout with fidelity information.
|
|
125
|
+
|
|
126
|
+
Creates a visualization of the quantum chip topology where nodes represent qubits
|
|
127
|
+
and edges represent connections between qubits. Edge thickness indicates gate errors
|
|
128
|
+
(thinner edges mean better fidelity) and selected qubits are highlighted in orange.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
cal_url: URL to retrieve calibration data from
|
|
132
|
+
qubit_layouts: List of qubit layouts where each layout is a list of qubit indices
|
|
133
|
+
station: Name of the quantum computing station to use predefined positions for.
|
|
134
|
+
If None, positions will be generated algorithmically.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
matplotlib.figure.Figure: The generated figure object containing the graph visualization
|
|
138
|
+
"""
|
|
139
|
+
edges_cal, fidelities_cal, topology = extract_fidelities(cal_url)
|
|
140
|
+
weights = -np.log(np.array(fidelities_cal))
|
|
141
|
+
edges_graph = [tuple(edge) + (weight,) for edge, weight in zip(edges_cal, weights)]
|
|
142
|
+
|
|
143
|
+
graph = PyGraph()
|
|
144
|
+
|
|
145
|
+
# Add nodes
|
|
146
|
+
nodes: set[int] = set()
|
|
147
|
+
for edge in edges_graph:
|
|
148
|
+
nodes.update(edge[:2])
|
|
149
|
+
graph.add_nodes_from(list(nodes))
|
|
150
|
+
|
|
151
|
+
# Add edges
|
|
152
|
+
graph.add_edges_from(edges_graph)
|
|
153
|
+
|
|
154
|
+
# Define qubit positions in plot
|
|
155
|
+
if station is not None and station.lower() in GraphPositions.predefined_stations:
|
|
156
|
+
pos = GraphPositions.predefined_stations[station.lower()]
|
|
157
|
+
else:
|
|
158
|
+
pos = GraphPositions.create_positions(graph, topology)
|
|
159
|
+
|
|
160
|
+
# Define node colors
|
|
161
|
+
node_colors = ["lightgrey" for _ in range(len(nodes))]
|
|
162
|
+
if qubit_layouts is not None:
|
|
163
|
+
for qb in {qb for layout in qubit_layouts for qb in layout}:
|
|
164
|
+
node_colors[qb] = "orange"
|
|
165
|
+
|
|
166
|
+
# Ensuring weights are in correct order for the plot
|
|
167
|
+
edge_list = graph.edge_list()
|
|
168
|
+
weights_dict = {}
|
|
169
|
+
edge_pos = set()
|
|
170
|
+
|
|
171
|
+
# Create a mapping between edge positions as defined in rustworkx and their weights
|
|
172
|
+
for e, w in zip(edge_list, weights):
|
|
173
|
+
pos_tuple = (tuple(pos[e[0]]), tuple(pos[e[1]]))
|
|
174
|
+
weights_dict[pos_tuple] = w
|
|
175
|
+
edge_pos.add(pos_tuple)
|
|
176
|
+
|
|
177
|
+
# Get corresponding weights in the same order
|
|
178
|
+
weights_ordered = np.array([weights_dict[edge] for edge in list(edge_pos)])
|
|
179
|
+
|
|
180
|
+
plt.subplots(figsize=(6, 6))
|
|
181
|
+
|
|
182
|
+
# Draw the graph
|
|
183
|
+
visualization.mpl_draw(
|
|
184
|
+
graph,
|
|
185
|
+
with_labels=True,
|
|
186
|
+
node_color=node_colors,
|
|
187
|
+
pos=pos,
|
|
188
|
+
labels=lambda node: node,
|
|
189
|
+
width=7 * weights_ordered / np.max(weights_ordered),
|
|
190
|
+
) # type: ignore[call-arg]
|
|
191
|
+
|
|
192
|
+
# Add edge labels using matplotlib's annotate
|
|
193
|
+
for edge in edges_graph:
|
|
194
|
+
x1, y1 = pos[edge[0]]
|
|
195
|
+
x2, y2 = pos[edge[1]]
|
|
196
|
+
x = (x1 + x2) / 2
|
|
197
|
+
y = (y1 + y2) / 2
|
|
198
|
+
plt.annotate(
|
|
199
|
+
f"{edge[2]:.1e}",
|
|
200
|
+
xy=(x, y),
|
|
201
|
+
xytext=(0, 0),
|
|
202
|
+
textcoords="offset points",
|
|
203
|
+
ha="center",
|
|
204
|
+
va="center",
|
|
205
|
+
bbox={"boxstyle": "round,pad=0.2", "fc": "white", "ec": "none", "alpha": 0.6},
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
plt.gca().invert_yaxis()
|
|
209
|
+
plt.title(
|
|
210
|
+
"Chip layout with selected qubits in orange\n"
|
|
211
|
+
+ "and gate errors indicated by edge thickness (thinner is better)"
|
|
212
|
+
)
|
|
213
|
+
plt.show()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def rx_to_nx_graph(backend_coupling_map: CouplingMap) -> nx.Graph:
|
|
217
|
+
"""Convert the Rustworkx graph returned by a backend to a Networkx graph.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
backend_coupling_map (CouplingMap): The coupling map of the backend.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
networkx.Graph: The Networkx Graph corresponding to the backend graph.
|
|
224
|
+
|
|
225
|
+
"""
|
|
226
|
+
# Generate a Networkx graph
|
|
227
|
+
graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
|
|
228
|
+
backend_egdes, backend_nodes = (list(graph_backend.edge_list()), list(graph_backend.node_indices()))
|
|
229
|
+
backend_nx_graph = nx.Graph()
|
|
230
|
+
backend_nx_graph.add_nodes_from(backend_nodes)
|
|
231
|
+
backend_nx_graph.add_edges_from(backend_egdes)
|
|
232
|
+
|
|
233
|
+
return backend_nx_graph
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Copyright 2025 IQM Benchmarks developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Shadow Tomography utility functions
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import random
|
|
20
|
+
from typing import Dict, List, Literal, Optional, Sequence, Tuple, cast
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
from numpy.random import RandomState
|
|
24
|
+
from qiskit import ClassicalRegister, QuantumCircuit, quantum_info
|
|
25
|
+
from qiskit.circuit.library import UnitaryGate
|
|
26
|
+
import scipy.linalg as spl
|
|
27
|
+
|
|
28
|
+
from iqm.benchmarks.utils import timeit
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def CUE(random_gen: RandomState, n: int) -> np.ndarray:
|
|
32
|
+
"""Prepares single qubit Haar-random unitary (drawn from Circuilar Unitary Ensemble - CUE).
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
random_gen (RandomState): a random generator.
|
|
36
|
+
n (int): the size of the matrix.
|
|
37
|
+
Returns:
|
|
38
|
+
np.ndarray: an n x n CUE matrix
|
|
39
|
+
"""
|
|
40
|
+
# Generate an N × N complex matrix Z with standard normal entries
|
|
41
|
+
Z = (random_gen.randn(n, n) + 1j * random_gen.randn(n, n)) / np.sqrt(2.0)
|
|
42
|
+
|
|
43
|
+
# Perform QR decomposition, Z = QR
|
|
44
|
+
Q, R = spl.qr(Z)
|
|
45
|
+
|
|
46
|
+
# Create the diagonal matrix Lambda
|
|
47
|
+
Lambda = np.diag(R.diagonal() / np.abs(R.diagonal()))
|
|
48
|
+
|
|
49
|
+
# Compute U = Q * Lambda, which is Haar-distributed
|
|
50
|
+
U = Q @ Lambda
|
|
51
|
+
|
|
52
|
+
return U
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@timeit
|
|
56
|
+
def local_shadow_tomography(
|
|
57
|
+
qc: QuantumCircuit,
|
|
58
|
+
Nu: int,
|
|
59
|
+
active_qubits: Sequence[int],
|
|
60
|
+
measure_other: Optional[Sequence[int]] = None,
|
|
61
|
+
measure_other_name: Optional[str] = None,
|
|
62
|
+
clifford_or_haar: Literal["clifford", "haar"] = "clifford",
|
|
63
|
+
cliffords_1q: Optional[Dict[str, QuantumCircuit]] = None,
|
|
64
|
+
) -> Tuple[np.ndarray | Dict[str, List[str]], List[QuantumCircuit]]:
|
|
65
|
+
"""
|
|
66
|
+
Prepares the circuits to perform local Haar or Clifford shadow tomography.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
qc (QuantumCircuit): The quantum circuit to which random unitaries are appended.
|
|
70
|
+
Nu (int): Number of local random unitaries used.
|
|
71
|
+
active_qubits (Sequence[int]): The Sequence of active qubits.
|
|
72
|
+
measure_other (Optional[Sequence[int]]): Whether to measure other qubits in the qc QuantumCircuit.
|
|
73
|
+
* Default is None.
|
|
74
|
+
measure_other_name (Optional[str]): Name of the classical register to assign measure_other.
|
|
75
|
+
clifford_or_haar (Literal["clifford", "haar"]): Whether to use Clifford or Haar random 1Q gates.
|
|
76
|
+
* Default is "clifford".
|
|
77
|
+
cliffords_1q (Optional[Dict[str, QuantumCircuit]]): Dictionary of 1-qubit Cliffords in terms of IQM-native r and CZ gates.
|
|
78
|
+
* Default is None.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
ValueError: If clifford_or_haar is not "clifford" or "haar".
|
|
82
|
+
ValueError: If cliffords_1q is None and clifford_or_haar is "clifford".
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Tuple[np.ndarray | Dict[str, List[str]], List[QuantumCircuit]]:
|
|
86
|
+
- ndarray | Dict[str, List[str]]: Either:
|
|
87
|
+
* Unitary gate (numpy ndarray), composed of local unitaries for each random initialisation and qubit, if clifford_or_haar == 'haar'.
|
|
88
|
+
* Dictionary of lists of Clifford labels corresponding to each RM, keys being str(qubit), if clifford_or_haar == 'clifford'.
|
|
89
|
+
- List[QuantumCircuit]: List of tomography circuits.
|
|
90
|
+
"""
|
|
91
|
+
if clifford_or_haar not in ["clifford", "haar"]:
|
|
92
|
+
raise ValueError("clifford_or_haar must be either 'clifford' or 'haar'.")
|
|
93
|
+
if clifford_or_haar == "clifford" and cliffords_1q is None:
|
|
94
|
+
raise ValueError("cliffords_1q dictionary must be provided if clifford_or_haar is 'clifford'.")
|
|
95
|
+
if clifford_or_haar == "clifford":
|
|
96
|
+
# Get the keys of the Clifford dictionaries
|
|
97
|
+
clifford_1q_keys = list(cast(Dict, cliffords_1q).keys())
|
|
98
|
+
|
|
99
|
+
qclist = []
|
|
100
|
+
seed = random.SystemRandom().randrange(2**32 - 1) # Init Random Generator
|
|
101
|
+
random_gen: RandomState = np.random.RandomState(seed) # pylint: disable=no-member
|
|
102
|
+
|
|
103
|
+
unitaries: Dict[str, List[str]] | np.ndarray
|
|
104
|
+
if clifford_or_haar == "haar":
|
|
105
|
+
unitaries = np.zeros((Nu, len(active_qubits), 2, 2), dtype=np.complex_)
|
|
106
|
+
else:
|
|
107
|
+
unitaries = {str(q): [] for q in active_qubits}
|
|
108
|
+
|
|
109
|
+
for u in range(Nu):
|
|
110
|
+
qc_copy = qc.copy()
|
|
111
|
+
for q_idx, qubit in enumerate(active_qubits):
|
|
112
|
+
if clifford_or_haar == "haar":
|
|
113
|
+
temp_U = CUE(random_gen, 2)
|
|
114
|
+
qc_copy.append(UnitaryGate(temp_U), [qubit])
|
|
115
|
+
cast(np.ndarray, unitaries)[u, q_idx, :, :] = np.array(temp_U)
|
|
116
|
+
elif clifford_or_haar == "clifford":
|
|
117
|
+
rand_key = random.choice(clifford_1q_keys)
|
|
118
|
+
c_1q = cast(dict, cliffords_1q)[rand_key]
|
|
119
|
+
qc_copy.compose(c_1q, qubits=[qubit], inplace=True)
|
|
120
|
+
cast(dict, unitaries)[str(qubit)].append(rand_key)
|
|
121
|
+
|
|
122
|
+
qc_copy.barrier()
|
|
123
|
+
|
|
124
|
+
register_rm = ClassicalRegister(len(active_qubits), "RMs")
|
|
125
|
+
qc_copy.add_register(register_rm)
|
|
126
|
+
qc_copy.measure(active_qubits, register_rm)
|
|
127
|
+
|
|
128
|
+
if measure_other is not None:
|
|
129
|
+
if measure_other_name is None:
|
|
130
|
+
measure_other_name = "non_RMs"
|
|
131
|
+
register_neighbors = ClassicalRegister(len(measure_other), measure_other_name)
|
|
132
|
+
qc_copy.add_register(register_neighbors)
|
|
133
|
+
qc_copy.measure(measure_other, register_neighbors)
|
|
134
|
+
|
|
135
|
+
qclist.append(qc_copy)
|
|
136
|
+
|
|
137
|
+
return unitaries, qclist
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_local_shadow(
|
|
141
|
+
counts: Dict[str, int],
|
|
142
|
+
unitary_arg: np.ndarray | Sequence[str],
|
|
143
|
+
subsystem_bit_indices: Sequence[int],
|
|
144
|
+
clifford_or_haar: Literal["clifford", "haar"] = "clifford",
|
|
145
|
+
cliffords_1q: Optional[Dict[str, QuantumCircuit]] = None,
|
|
146
|
+
) -> np.ndarray:
|
|
147
|
+
"""Constructs shadows for each individual initialisation.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
counts (Dict[str, int]): a dictionary of bit-string counts.
|
|
151
|
+
unitary_arg (np.ndarray | Sequence[str]): local random unitaries used for a given initialisation, either specified as
|
|
152
|
+
- a numpy array, or
|
|
153
|
+
- a Sequence of Clifford labels.
|
|
154
|
+
subsystem_bit_indices (Sequence[int]): Bit indices in the counts of the subsystem to construct the shadow of.
|
|
155
|
+
clifford_or_haar (Literal["clifford", "haar"]): Whether to use Clifford or Haar random 1Q gates.
|
|
156
|
+
* Default is "clifford".
|
|
157
|
+
cliffords_1q (Optional[Dict[str, QuantumCircuit]]): dictionary of 1-qubit Cliffords in terms of IQM-native r and CZ gates
|
|
158
|
+
* Default is None.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
np.ndarray: shadow of considered subsystem.
|
|
162
|
+
"""
|
|
163
|
+
if clifford_or_haar not in ["clifford", "haar"]:
|
|
164
|
+
raise ValueError("clifford_or_haar must be either 'clifford' or 'haar'.")
|
|
165
|
+
if clifford_or_haar == "clifford" and cliffords_1q is None:
|
|
166
|
+
raise ValueError("cliffords_1q dictionary must be provided if clifford_or_haar is 'clifford'.")
|
|
167
|
+
if clifford_or_haar == "haar" and isinstance(unitary_arg, Sequence):
|
|
168
|
+
raise ValueError("If clifford_or_haar is 'haar', the unitary operator must be a numpy array.")
|
|
169
|
+
if clifford_or_haar == "clifford" and not isinstance(unitary_arg, Sequence):
|
|
170
|
+
raise ValueError(
|
|
171
|
+
"If clifford_or_haar is 'clifford', the unitary operator must be specified as a Sequence of strings."
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
nqubits = len(subsystem_bit_indices)
|
|
175
|
+
rhoshadows = np.zeros([2**nqubits, 2**nqubits], dtype=complex)
|
|
176
|
+
proj = np.zeros((2, 2, 2), dtype=complex)
|
|
177
|
+
proj[0, :, :] = np.array([[1, 0], [0, 0]])
|
|
178
|
+
proj[1, :, :] = np.array([[0, 0], [0, 1]])
|
|
179
|
+
shots = sum(list(counts.values()))
|
|
180
|
+
|
|
181
|
+
unitary_op: np.ndarray
|
|
182
|
+
if clifford_or_haar == "haar":
|
|
183
|
+
unitary_op = cast(np.ndarray, unitary_arg)
|
|
184
|
+
else:
|
|
185
|
+
unitary_op = np.zeros((nqubits, 2, 2), dtype=complex)
|
|
186
|
+
for qubit_idx, clif_label in enumerate(unitary_arg):
|
|
187
|
+
unitary_op[qubit_idx, :, :] = quantum_info.Operator(cast(dict, cliffords_1q)[clif_label]).to_matrix()
|
|
188
|
+
|
|
189
|
+
for bit_strings in counts.keys():
|
|
190
|
+
rho_j: int | np.ndarray = 1
|
|
191
|
+
for j in subsystem_bit_indices:
|
|
192
|
+
s_j = int(bit_strings[::-1][j])
|
|
193
|
+
rho_j = np.kron(
|
|
194
|
+
rho_j,
|
|
195
|
+
3
|
|
196
|
+
* np.einsum(
|
|
197
|
+
"ab,bc,cd", np.transpose(np.conjugate(unitary_op[j, :, :])), proj[s_j, :, :], unitary_op[j, :, :]
|
|
198
|
+
)
|
|
199
|
+
- np.array([[1, 0], [0, 1]]),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
rhoshadows += rho_j * counts[bit_strings] / shots
|
|
203
|
+
|
|
204
|
+
return rhoshadows
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def get_negativity(rho: np.ndarray, NA: int, NB: int) -> float:
|
|
208
|
+
"""Computes the negativity of a given density matrix.
|
|
209
|
+
Note that a negativity >0 is only a necessary and sufficient condition for entanglement if NA = NB = 1. For more qubits per subsystems it is merely a necessary condition.
|
|
210
|
+
Args:
|
|
211
|
+
rho (np.ndarray): Density matrix.
|
|
212
|
+
NA (int): Number of qubits for subsystem A.
|
|
213
|
+
NB (int): Number of qubits for subsystem B.
|
|
214
|
+
Returns:
|
|
215
|
+
float: the negativity of the input density matrix.
|
|
216
|
+
"""
|
|
217
|
+
da = 2**NA
|
|
218
|
+
db = 2**NB
|
|
219
|
+
rho = rho.reshape(da, db, da, db)
|
|
220
|
+
# TODO: # pylint: disable=fixme
|
|
221
|
+
# This is a one-liner here, but generally it would be nicer to have
|
|
222
|
+
# a partial transpose function w.r.t. any subsystem in the utils file!
|
|
223
|
+
rho_t = np.einsum("ijkl -> kjil", rho)
|
|
224
|
+
rho_t = rho_t.reshape(2 ** (NA + NB), 2 ** (NA + NB))
|
|
225
|
+
evals, _ = np.linalg.eig(rho_t)
|
|
226
|
+
neg = np.sum([np.abs(i) - i for i in np.real(evals)]) / 2
|
|
227
|
+
|
|
228
|
+
return neg
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iqm-benchmarks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.28
|
|
4
4
|
Summary: A package for implementation of Quantum Characterization, Verification and Validation (QCVV) techniques on IQM's hardware at gate level abstraction
|
|
5
5
|
Author-email: IQM Finland Oy <developers@meetiqm.com>, Adrian Auer <adrian.auer@meetiqm.com>, Raphael Brieger <raphael.brieger@meetiqm.com>, Alessio Calzona <alessio.calzona@meetiqm.com>, Pedro Figueroa Romero <pedro.romero@meetiqm.com>, Amin Hosseinkhani <amin.hosseinkhani@meetiqm.com>, Miikka Koistinen <miikka@meetiqm.com>, Nadia Milazzo <nadia.milazzo@meetiqm.com>, Vicente Pina Canelles <vicente.pina@meetiqm.com>, Aniket Rath <aniket.rath@meetiqm.com>, Jami Rönkkö <jami@meetiqm.com>, Stefan Seegerer <stefan.seegerer@meetiqm.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/iqm-finland/iqm-benchmarks
|
|
@@ -16,10 +16,11 @@ Requires-Dist: matplotlib<4,>=3.6.3
|
|
|
16
16
|
Requires-Dist: more-itertools<11.0.0,>=10.1.0
|
|
17
17
|
Requires-Dist: mthree<2.7,>=2.6
|
|
18
18
|
Requires-Dist: networkx<4.0,>=3.3
|
|
19
|
-
Requires-Dist: rustworkx>=0.
|
|
19
|
+
Requires-Dist: rustworkx>=0.16.0
|
|
20
20
|
Requires-Dist: numpy<2.0,>=1.25.2
|
|
21
|
-
Requires-Dist: qiskit<2.0,>=1.
|
|
22
|
-
Requires-Dist: qiskit-iqm<
|
|
21
|
+
Requires-Dist: qiskit<2.0,>=1.2.4
|
|
22
|
+
Requires-Dist: qiskit-iqm<18.0,>=17.8
|
|
23
|
+
Requires-Dist: iqm-client>=22.7
|
|
23
24
|
Requires-Dist: scikit-optimize<0.11.0,>=0.10.2
|
|
24
25
|
Requires-Dist: tabulate<1.0.0,>=0.9.0
|
|
25
26
|
Requires-Dist: uncertainties<3.3.0,>=3.2.2
|
|
@@ -68,6 +69,7 @@ Below is a list of the benchmarks currently available in the suite:
|
|
|
68
69
|
- CLOPS [[arXiv:2110.14108 [quant-ph]](https://arxiv.org/abs/2110.14108) (2021)]
|
|
69
70
|
* Entanglement:
|
|
70
71
|
- GHZ State Fidelity [[arXiv:0712.0921 [quant-ph]](https://arxiv.org/abs/0712.0921) (2007)]
|
|
72
|
+
- Graph State Bipartite Entanglement [[Adv. Quantum Technol., 2100061](https://doi.org/10.1002/qute.202100061) (2021)]
|
|
71
73
|
* Optimization:
|
|
72
74
|
- Q-Score [[IEEE Trans. Quantum Eng., 2](https://doi.org/10.1109/TQE.2021.3090207) (2021)]
|
|
73
75
|
|
|
@@ -1,43 +1,46 @@
|
|
|
1
|
-
iqm/benchmarks/__init__.py,sha256=
|
|
1
|
+
iqm/benchmarks/__init__.py,sha256=74NuBycITM-CV7nx0l1T2wbnQDdVmF8jTZVE0KpG7pc,2773
|
|
2
2
|
iqm/benchmarks/benchmark.py,sha256=3E7g7RQjCIGIpSI1gOSrI3V9SAVs-XEOMrPgToK_7vw,4972
|
|
3
3
|
iqm/benchmarks/benchmark_definition.py,sha256=e4xe0wlWKZqj48_6-zTglMaMeoiA9aGkHrrSgoCfPkM,11271
|
|
4
4
|
iqm/benchmarks/circuit_containers.py,sha256=anEtZEsodYqOX-34oZRmuKGeEpp_VfgG5045Mz4-4hI,7562
|
|
5
5
|
iqm/benchmarks/logging_config.py,sha256=U7olP5Kr75AcLJqNODf9VBhJLVqIvA4AYR6J39D5rww,1052
|
|
6
6
|
iqm/benchmarks/readout_mitigation.py,sha256=Q2SOGWTNgmklOYkNxepAaSaXlxSj0QQyymYY1bOkT8A,11756
|
|
7
|
-
iqm/benchmarks/utils.py,sha256=
|
|
7
|
+
iqm/benchmarks/utils.py,sha256=2aEwFhZAHBmqLjSMtnqjuWcXCyMX3vpUfF-vqoeBIHw,38517
|
|
8
|
+
iqm/benchmarks/utils_plots.py,sha256=Y2AQdY92hopDEWmM5KUvF79OqEBTNAUgahwomjhEKZE,8036
|
|
9
|
+
iqm/benchmarks/utils_shadows.py,sha256=e77PV_uaAO5m_woox9lAzompKAvFeDJ-0AKJrNJ7NFg,9728
|
|
8
10
|
iqm/benchmarks/compressive_gst/__init__.py,sha256=LneifgYXtcwo2jcXo7GdUEHL6_peipukShhkrdaTRCA,929
|
|
9
11
|
iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=2kiRttog4jR-vtMHu847GTFe5qL_i_uYr_4WMGqt9Ww,25653
|
|
10
|
-
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=
|
|
11
|
-
iqm/benchmarks/entanglement/__init__.py,sha256=
|
|
12
|
-
iqm/benchmarks/entanglement/ghz.py,sha256=
|
|
12
|
+
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=jA1MaXIyPBwxbN36mTjn_jKaRO5-NdoG0YV-RaujU3s,36450
|
|
13
|
+
iqm/benchmarks/entanglement/__init__.py,sha256=sHVVToRWRCz0LSntk1rQaoSNNeyZLPoiTjUKWZWrk1E,778
|
|
14
|
+
iqm/benchmarks/entanglement/ghz.py,sha256=RGA6ynJFsfaCJv0nKccsiIzPk2G-iHHvIeW8LVu30HY,41249
|
|
15
|
+
iqm/benchmarks/entanglement/graph_states.py,sha256=okWdpASQewZ62UaHks2N-tvCgCsUtIkz8dBoX1xh6Jk,62512
|
|
13
16
|
iqm/benchmarks/optimization/__init__.py,sha256=_ajW_OibYLCtzU5AUv5c2zuuVYn8ZNeZUcUUSIGt51M,747
|
|
14
|
-
iqm/benchmarks/optimization/qscore.py,sha256=
|
|
17
|
+
iqm/benchmarks/optimization/qscore.py,sha256=KOw8fjXeMwCjYvKukpX7IiAqRQrVTkrnIgKjNCPVWdw,38130
|
|
15
18
|
iqm/benchmarks/quantum_volume/__init__.py,sha256=i-Q4SpDWELBw7frXnxm1j4wJRcxbIyrS5uEK_v06YHo,951
|
|
16
|
-
iqm/benchmarks/quantum_volume/clops.py,sha256=
|
|
17
|
-
iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=
|
|
19
|
+
iqm/benchmarks/quantum_volume/clops.py,sha256=fD_eh10qHv_W-_mCA-PjPDIxNJ5sqD7yDkK1UjRsAAo,31474
|
|
20
|
+
iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=pro7Lz-A5pPpT9UZ8wtXKTyhdWmTjQjRHt4BylDR-3U,36553
|
|
18
21
|
iqm/benchmarks/randomized_benchmarking/__init__.py,sha256=IkKo-7zUChxZZd3my_csQCJfJfZNsV3-JTvdG8uqys4,734
|
|
19
|
-
iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl,sha256=
|
|
20
|
-
iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl,sha256=
|
|
22
|
+
iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl,sha256=yrmSJqhv7Lb1yqiqU9-2baqTljJPNmTUPQR-AH6GGfc,7800
|
|
23
|
+
iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl,sha256=mJQLubWPOb-DbmFi4oKYJqAMW_Yyo3eJjRjLGl9Sqmo,10282247
|
|
21
24
|
iqm/benchmarks/randomized_benchmarking/multi_lmfit.py,sha256=Se1ygR4mXn_2_P82Ch31KBnCmY-g_A9NKzE9Ir8nEvw,3247
|
|
22
|
-
iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py,sha256=
|
|
25
|
+
iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py,sha256=MryufwrOCBcZZHDeX9WGHVtYhFz2XtS3xLm6YQYEAM4,43160
|
|
23
26
|
iqm/benchmarks/randomized_benchmarking/clifford_rb/__init__.py,sha256=bTDA156LAl7OLGcMec--1nzDrV1XpPRVq3CquTmucgE,677
|
|
24
27
|
iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py,sha256=IGBrq_a9eaVPknkBLKHKS4BOcumHn6TZdasUNKTZjGI,18685
|
|
25
28
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py,sha256=sq6MgN_hwlpkOj10vyCU4e6eKSX-oLcF2L9na6W2Gt4,681
|
|
26
29
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=TaR1YFWBhOgm1hmEQzuwLYpp0yl0Xpuo3jAT6YhiXpc,28471
|
|
27
30
|
iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=ZekEqI_89nXzGO1vjM-b5Uwwicy59M4fYHXfA-f0MIg,674
|
|
28
|
-
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=
|
|
29
|
-
iqm_benchmarks-2.
|
|
31
|
+
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=_xdp8XLPcZyuqy7Xz8-K8H7zjgRo9ZxFiDgCXE72gaE,34997
|
|
32
|
+
iqm_benchmarks-2.28.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
|
|
30
33
|
mGST/LICENSE,sha256=TtHNq55cUcbglb7uhVudeBLUh_qPdUoAEvU0BBwFz-k,1098
|
|
31
34
|
mGST/README.md,sha256=v_5kw253csHF4-RfE-44KqFmBXIsSMRmOtN0AUPrRxE,5050
|
|
32
35
|
mGST/additional_fns.py,sha256=_SEJ10FRNM7_CroysT8hCLZTfpm6ZhEIDCY5zPTnhjo,31390
|
|
33
|
-
mGST/algorithm.py,sha256=
|
|
36
|
+
mGST/algorithm.py,sha256=QnxLJtxZysggLUtbZE-c2MWaFclZB2XUVJhvrmUvjVs,26315
|
|
34
37
|
mGST/compatibility.py,sha256=00DsPnNfOtrQcDTvxBDs-0aMhmuXmOIIxl_Ohy-Emkg,8920
|
|
35
38
|
mGST/low_level_jit.py,sha256=uE1D3v01FbPpsbP92C4220OQalzOfxgL1Ku89BNkxLY,27377
|
|
36
39
|
mGST/optimization.py,sha256=YHwkzIkYvsZOPjclR-BCQWh24jeqjuXp0BB0WX5Lwow,10559
|
|
37
40
|
mGST/qiskit_interface.py,sha256=ajx6Zn5FnrX_T7tMP8xnBLyG4c2ddFRm0Fu2_3r1t30,10118
|
|
38
41
|
mGST/reporting/figure_gen.py,sha256=6Xd8vwfy09hLY1YbJY6TRevuMsQSU4MsWqemly3ZO0I,12970
|
|
39
42
|
mGST/reporting/reporting.py,sha256=B8NWfpZrrSmyH7lwZxd0EbZMYLsAGK1YsHRB4D5qXH4,26002
|
|
40
|
-
iqm_benchmarks-2.
|
|
41
|
-
iqm_benchmarks-2.
|
|
42
|
-
iqm_benchmarks-2.
|
|
43
|
-
iqm_benchmarks-2.
|
|
43
|
+
iqm_benchmarks-2.28.dist-info/METADATA,sha256=sRwA84uA95W9hBB-hy5KYVZFvOg6eSwhQVC9nTjMFLw,10710
|
|
44
|
+
iqm_benchmarks-2.28.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
45
|
+
iqm_benchmarks-2.28.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
|
|
46
|
+
iqm_benchmarks-2.28.dist-info/RECORD,,
|
mGST/algorithm.py
CHANGED
|
@@ -727,7 +727,7 @@ def run_mGST(
|
|
|
727
727
|
qcvv_logger.info(f"Convergence criterion satisfied")
|
|
728
728
|
else:
|
|
729
729
|
qcvv_logger.info(
|
|
730
|
-
f"Convergence criterion not satisfied,inspect results and consider increasing max_iter or using new initializations.",
|
|
730
|
+
f"Convergence criterion not satisfied, inspect results and consider increasing max_iter or using new initializations.",
|
|
731
731
|
)
|
|
732
732
|
qcvv_logger.info(
|
|
733
733
|
f"Final objective {Decimal(res_list[-1]):.2e} in time {(time.time() - t0):.2f}s",
|
|
File without changes
|
|
File without changes
|