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.

@@ -17,9 +17,10 @@ Plotting and visualization utility functions
17
17
  """
18
18
  from dataclasses import dataclass
19
19
  import os
20
- from typing import Dict, List, Literal, Optional, Sequence, Tuple
20
+ from typing import Dict, List, Literal, Optional, Sequence, Tuple, cast
21
21
 
22
22
  from matplotlib.figure import Figure
23
+ from matplotlib.patches import Circle, FancyBboxPatch
23
24
  import matplotlib.pyplot as plt
24
25
  import networkx as nx
25
26
  import numpy as np
@@ -40,9 +41,9 @@ class GraphPositions:
40
41
  provides methods to generate positions for different layout types.
41
42
 
42
43
  Attributes:
43
- garnet_positions (Dict[int, Tuple[int, int]]): Mapping of node indices to (x,y) positions for Garnet chip.
44
- deneb_positions (Dict[int, Tuple[int, int]]): Mapping of node indices to (x,y) positions for Deneb chip.
45
- predefined_stations (Dict[str, Dict[int, Tuple[int, int]]]): Mapping of chip names to their position dictionaries.
44
+ garnet_positions (Dict[int, Tuple[float, float]]): Mapping of node indices to (x,y) positions for Garnet chip.
45
+ deneb_positions (Dict[int, Tuple[float, float]]): Mapping of node indices to (x,y) positions for Deneb chip.
46
+ predefined_stations (Dict[str, Dict[int, Tuple[float, float]]]): Mapping of chip names to their position dictionaries.
46
47
  """
47
48
 
48
49
  garnet_positions = {
@@ -68,15 +69,116 @@ class GraphPositions:
68
69
  19: (3.0, 1.0),
69
70
  }
70
71
 
72
+ emerald_positions = {
73
+ 0: (10, 10),
74
+ 1: (11, 9),
75
+ 2: (7, 11),
76
+ 3: (8, 10),
77
+ 4: (9, 9),
78
+ 5: (10, 8),
79
+ 6: (11, 7),
80
+ 7: (5, 11),
81
+ 8: (6, 10),
82
+ 9: (7, 9),
83
+ 10: (8, 8),
84
+ 11: (9, 7),
85
+ 12: (10, 6),
86
+ 13: (11, 5),
87
+ 14: (3, 11),
88
+ 15: (4, 10),
89
+ 16: (5, 9),
90
+ 17: (6, 8),
91
+ 18: (7, 7),
92
+ 19: (8, 6),
93
+ 20: (9, 5),
94
+ 21: (10, 4),
95
+ 22: (2, 10),
96
+ 23: (3, 9),
97
+ 24: (4, 8),
98
+ 25: (5, 7),
99
+ 26: (6, 6),
100
+ 27: (7, 5),
101
+ 28: (8, 4),
102
+ 29: (9, 3),
103
+ 30: (10, 2),
104
+ 31: (2, 8),
105
+ 32: (3, 7),
106
+ 33: (4, 6),
107
+ 34: (5, 5),
108
+ 35: (6, 4),
109
+ 36: (7, 3),
110
+ 37: (8, 2),
111
+ 38: (9, 1),
112
+ 39: (1, 7),
113
+ 40: (2, 6),
114
+ 41: (3, 5),
115
+ 42: (4, 4),
116
+ 43: (5, 3),
117
+ 44: (6, 2),
118
+ 45: (7, 1),
119
+ 46: (1, 5),
120
+ 47: (2, 4),
121
+ 48: (3, 3),
122
+ 49: (4, 2),
123
+ 50: (5, 1),
124
+ 51: (1, 3),
125
+ 52: (2, 2),
126
+ 53: (3, 1),
127
+ }
128
+
129
+ sirius_positions = {
130
+ # Even nodes on the bottom
131
+ 2: (1, 5),
132
+ 4: (3, 5),
133
+ 6: (5, 5),
134
+ 8: (7, 5),
135
+ 10: (9, 5),
136
+ 12: (11, 5),
137
+ 14: (13, 5),
138
+ 16: (15, 5),
139
+ 18: (17, 5),
140
+ 20: (19, 5),
141
+ 22: (21, 5),
142
+ 24: (23, 5),
143
+ # Odd nodes on the top
144
+ 1: (1, 1),
145
+ 3: (3, 1),
146
+ 5: (5, 1),
147
+ 7: (7, 1),
148
+ 9: (9, 1),
149
+ 11: (11, 1),
150
+ 13: (13, 1),
151
+ 15: (15, 1),
152
+ 17: (17, 1),
153
+ 19: (19, 1),
154
+ 21: (21, 1),
155
+ 23: (23, 1),
156
+ }
157
+ # Add dummy indices for the resonator
158
+ max_qubit_number = np.max(list(sirius_positions.keys()))
159
+ previous_nodes = list(sirius_positions.keys())
160
+ for node in previous_nodes:
161
+ sirius_positions.update({node + max_qubit_number: (sirius_positions[node][0], 3)})
162
+ # Node 0 in the middle
163
+ sirius_positions.update({0: (16.5, 3)})
164
+
71
165
  deneb_positions = {
72
- 0: (2.0, 2.0),
73
- 1: (1.0, 1.0),
74
- 3: (2.0, 1.0),
75
- 5: (3.0, 1.0),
76
- 2: (1.0, 3.0),
77
- 4: (2.0, 3.0),
78
- 6: (3.0, 3.0),
166
+ # Even nodes on the bottom
167
+ 2: (1, 5),
168
+ 4: (3, 5),
169
+ 6: (5, 5),
170
+ # Odd nodes on the top
171
+ 1: (1, 1),
172
+ 3: (3, 1),
173
+ 5: (5, 1),
79
174
  }
175
+ # Add dummy indices for the resonator
176
+ max_qubit_number = np.max(list(deneb_positions.keys()))
177
+ previous_nodes = list(deneb_positions.keys())
178
+ for node in previous_nodes:
179
+ deneb_positions.update({node + max_qubit_number: (deneb_positions[node][0], 3)})
180
+ # Node 0 in the middle
181
+ deneb_positions.update({0: (2.5, 3)})
80
182
 
81
183
  predefined_stations = {
82
184
  "garnet": garnet_positions,
@@ -85,6 +187,8 @@ class GraphPositions:
85
187
  "deneb": deneb_positions,
86
188
  "fakedeneb": deneb_positions,
87
189
  "iqmfakedeneb": deneb_positions,
190
+ "emerald": emerald_positions,
191
+ "sirius": sirius_positions,
88
192
  }
89
193
 
90
194
  @staticmethod
@@ -128,6 +232,40 @@ class GraphPositions:
128
232
  }
129
233
  return pos
130
234
 
235
+ @staticmethod
236
+ def get_positions(
237
+ station: Optional[str] = None, graph: Optional[PyGraph] = None, num_qubits: Optional[int] = None
238
+ ) -> Dict[int, Tuple[float, float]]:
239
+ """Get predefined positions for a specific station or generate positions for a custom graph.
240
+
241
+ Args:
242
+ station (Optional[str]): The name of the station to get predefined positions for.
243
+ If None, positions will be generated algorithmically.
244
+ graph (Optional[PyGraph]): The graph to generate positions for if no predefined positions exist.
245
+ Used only when station is None and num_qubits doesn't match any predefined layout.
246
+ num_qubits (Optional[int]): The number of qubits to get a layout for.
247
+ If matches a known system, predefined positions will be used.
248
+
249
+ Returns:
250
+ Dict[int, Tuple[float, float]]: A dictionary mapping node indices to (x,y) coordinates.
251
+
252
+ Raises:
253
+ ValueError: If none of station, graph, or num_qubits are provided, or if num_qubits doesn't
254
+ match any predefined layout and graph is None.
255
+ """
256
+ if station is not None and station.lower() in GraphPositions.predefined_stations:
257
+ qubit_positions = cast(Dict[int, Tuple[float, float]], GraphPositions.predefined_stations[station.lower()])
258
+ else:
259
+ qubit_station_dict = {6: "deneb", 7: "deneb", 20: "garnet", 24: "sirius", 17: "sirius", 54: "emerald"}
260
+ if num_qubits is not None and num_qubits in qubit_station_dict:
261
+ station = qubit_station_dict[num_qubits]
262
+ qubit_positions = cast(Dict[int, Tuple[float, float]], GraphPositions.predefined_stations[station])
263
+ elif graph is not None:
264
+ qubit_positions = GraphPositions.create_positions(graph)
265
+ else:
266
+ raise ValueError("Either a station name, a graph, or a qubit count must be provided to get positions.")
267
+ return qubit_positions
268
+
131
269
 
132
270
  def draw_graph_edges(
133
271
  backend_coupling_map: CouplingMap,
@@ -165,23 +303,7 @@ def draw_graph_edges(
165
303
  fig = plt.figure()
166
304
  ax = plt.axes()
167
305
 
168
- if station is not None:
169
- if station.lower() in GraphPositions.predefined_stations:
170
- qubit_positions = GraphPositions.predefined_stations[station.lower()]
171
- else:
172
- if backend_num_qubits in (6, 20):
173
- station = "garnet" if backend_num_qubits == 20 else "deneb"
174
- qubit_positions = GraphPositions.predefined_stations[station]
175
- else:
176
- graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
177
- qubit_positions = GraphPositions.create_positions(graph_backend)
178
- else:
179
- graph_backend = backend_coupling_map.graph.to_undirected(multigraph=False)
180
- if backend_num_qubits in (6, 20):
181
- station = "garnet" if backend_num_qubits == 20 else "deneb"
182
- qubit_positions = GraphPositions.predefined_stations[station]
183
- else:
184
- qubit_positions = GraphPositions.create_positions(graph_backend)
306
+ qubit_positions = GraphPositions.get_positions(station=station, graph=None, num_qubits=backend_num_qubits)
185
307
 
186
308
  label_station = station if station is not None else f"{backend_num_qubits}-qubit IQM Backend"
187
309
  if disjoint_layers is None:
@@ -226,6 +348,7 @@ def draw_graph_edges(
226
348
  f"\n{timestamp}"
227
349
  )
228
350
  ax.set_aspect(0.925)
351
+ plt.gca().invert_yaxis()
229
352
  plt.close()
230
353
 
231
354
  return fig_name, fig
@@ -302,8 +425,55 @@ def evaluate_hamiltonian_paths(
302
425
  return path_costs
303
426
 
304
427
 
428
+ def calculate_node_radii(metric_dict: Dict[str, Dict[int, float]], qubit_nodes: List[int], sq_metric: str) -> np.ndarray:
429
+ """Calculate node radii based on the specified single qubit metric. For the coherence metric, the fidelity is calculated as the idling fidelity of a single qubit gate duration.
430
+
431
+ Args:
432
+ metric_dict (Dict[str, Dict[int, float]]): Dictionary containing various qubit metrics.
433
+ qubit_nodes (List[int]): List of qubits to calculate the radius for.
434
+ sq_metric (str): Metric to use for radius calculation.
435
+ Options: "fidelity", "coherence", or "readout".
436
+
437
+ Returns:
438
+ numpy.ndarray: Array of radii values for each qubit node.
439
+
440
+ Raises:
441
+ ValueError: If an unsupported metric type is provided.
442
+ """
443
+ if sq_metric == "fidelity":
444
+ radii = -np.log(np.array([metric_dict["fidelity_1qb_gates_averaged"][node] for node in qubit_nodes]))
445
+ if "fidelity_1qb_gates_averaged" not in metric_dict:
446
+ raise ValueError(
447
+ "The metric 'fidelity_1qb_gates_averaged' is not available in the backend metrics."
448
+ )
449
+ elif sq_metric == "coherence":
450
+ if "t1_time" not in metric_dict or "t2_time" not in metric_dict:
451
+ raise ValueError(
452
+ "At least one of the metrics 't1_time' and 't2_time' is not available in the backend metrics."
453
+ )
454
+ sqg_time = 32e-9
455
+ t1_times = [metric_dict["t1_time"][node] for node in qubit_nodes]
456
+ t2_times = [metric_dict["t2_time"][node] for node in qubit_nodes]
457
+ idle_fidelities = (3 + np.exp(-sqg_time / np.array(t1_times)) + 2 * np.exp(-sqg_time / np.array(t2_times))) / 6
458
+ radii = -np.log(idle_fidelities)
459
+ elif sq_metric == "readout":
460
+ if "single_shot_readout_fidelity" not in metric_dict:
461
+ raise ValueError(
462
+ "The metric 'single_shot_readout_fidelity' is both available in the backend metrics."
463
+ )
464
+ readout_fidelities = [metric_dict["single_shot_readout_fidelity"][node] for node in qubit_nodes]
465
+ radii = -np.log(readout_fidelities)
466
+ else:
467
+ raise ValueError(
468
+ f"Unsupported single qubit metric: {sq_metric}, supported metrics are: fidelity, coherence, readout"
469
+ )
470
+ return radii
471
+
305
472
  def plot_layout_fidelity_graph(
306
- cal_url: str, qubit_layouts: Optional[list[list[int]]] = None, station: Optional[str] = None
473
+ cal_url: str,
474
+ qubit_layouts: Optional[list[list[int]]] = None,
475
+ station: Optional[str] = None,
476
+ sq_metric: Optional[str] = "coherence",
307
477
  ):
308
478
  """Plot a graph showing the quantum chip layout with fidelity information.
309
479
 
@@ -316,57 +486,75 @@ def plot_layout_fidelity_graph(
316
486
  qubit_layouts: List of qubit layouts where each layout is a list of qubit indices
317
487
  station: Name of the quantum computing station to use predefined positions for.
318
488
  If None, positions will be generated algorithmically.
489
+ sq_metric: Optional single qubit metric to use for the visualization, can be either "fidelity", "coherence",
490
+ or "readout".
319
491
 
320
492
  Returns:
321
493
  matplotlib.figure.Figure: The generated figure object containing the graph visualization
322
494
  """
323
- edges_cal, fidelities_cal, topology = extract_fidelities(cal_url)
324
- weights = -np.log(np.array(fidelities_cal))
325
- edges_graph = [tuple(edge) + (weight,) for edge, weight in zip(edges_cal, weights)]
326
-
327
- graph = PyGraph()
328
-
329
- # Add nodes
330
- nodes: set[int] = set()
331
- for edge in edges_graph:
332
- nodes.update(edge[:2])
333
- graph.add_nodes_from(list(nodes))
495
+ # pylint: disable=unbalanced-tuple-unpacking, disable=too-many-statements
496
+ edges_cal, fidelities_cal, topology, qubit_mapping, metric_dict = extract_fidelities(cal_url, all_metrics=True)
497
+ if topology == "star":
498
+ idx_to_qubit = {idx: qubit for qubit, idx in qubit_mapping.items()}
499
+ qubit_nodes = list(idx_to_qubit.keys())[1:]
500
+ fig, ax = plt.subplots(figsize=(len(qubit_nodes), 3))
501
+ else:
502
+ # For other topologies, qubits are indexed starting from 0 as per the Qiskit convention
503
+ idx_to_qubit = {idx: qubit - 1 for qubit, idx in qubit_mapping.items()}
504
+ qubit_nodes = list(idx_to_qubit.keys())
505
+ fig, ax = plt.subplots(figsize=(1.5 * np.sqrt(len(qubit_nodes)), 1.5 * np.sqrt(len(qubit_nodes))))
334
506
 
335
- # Add edges
336
- graph.add_edges_from(edges_graph)
507
+ weights = -np.log(np.array(fidelities_cal))
508
+ calibrated_nodes = list(idx_to_qubit.keys())
337
509
 
338
510
  # Define qubit positions in plot
339
- if station is not None and station.lower() in GraphPositions.predefined_stations:
340
- pos = GraphPositions.predefined_stations[station.lower()]
341
- else:
342
- pos = GraphPositions.create_positions(graph, topology)
511
+ qubit_positions = GraphPositions.get_positions(station=station, graph=None, num_qubits=len(calibrated_nodes))
343
512
 
344
- # Define node colors
345
- node_colors = ["lightgrey" for _ in range(len(nodes))]
346
- if qubit_layouts is not None:
347
- for qb in {qb for layout in qubit_layouts for qb in layout}:
348
- node_colors[qb] = "orange"
349
-
350
- plt.subplots(figsize=(1.5 * np.sqrt(len(nodes)), 1.5 * np.sqrt(len(nodes))))
513
+ graph = PyGraph()
514
+ nodes = list(set(qubit_positions.keys()))
515
+ graph.add_nodes_from(nodes)
516
+ for edge, weight in zip(edges_cal, weights):
517
+ if topology == "star":
518
+ max_qubit_number = (np.max(list(qubit_positions.keys())) + 1) // 2
519
+ graph.add_edge(idx_to_qubit[edge[0]], idx_to_qubit[edge[0]] + max_qubit_number, weight)
520
+ else:
521
+ graph.add_edge(idx_to_qubit[edge[0]], idx_to_qubit[edge[1]], weight)
351
522
 
352
- # Draw the graph
523
+ # Draw the main graph
353
524
  visualization.mpl_draw(
354
525
  graph,
526
+ ax=ax,
355
527
  with_labels=True,
356
- node_color=node_colors,
357
- pos=pos,
528
+ node_color="none", # No node color since we're using circles
529
+ pos=qubit_positions,
358
530
  labels=lambda node: node,
359
- width=7 * weights / np.max(weights),
531
+ font_color="white",
532
+ width=graph.edges() / np.max(graph.edges()) * 10,
360
533
  ) # type: ignore[call-arg]
361
534
 
535
+ # Draw nodes as circles with varying radii given by the single qubit metric
536
+ radii = calculate_node_radii(metric_dict, qubit_nodes, sq_metric)
537
+ node_colors = ["darkgray" for _ in range(len(nodes))]
538
+ if qubit_layouts is not None:
539
+ for qb in {qb for layout in qubit_layouts for qb in layout}:
540
+ node_colors[qb] = "orange"
541
+ max_radius = 0.12 + np.max(radii) / np.max(radii) / 2.5
542
+
543
+ for idx, node in enumerate(qubit_nodes):
544
+ position = qubit_positions[idx_to_qubit[node]]
545
+ radius = 0.12 + radii[idx] / np.max(radii) / 2.5
546
+ circle = Circle(position, radius=radius, color=node_colors[idx_to_qubit[node]], fill=True, alpha=1)
547
+ ax.add_patch(circle)
548
+
362
549
  # Add edge labels using matplotlib's annotate
363
- for edge in edges_graph:
364
- x1, y1 = pos[edge[0]]
365
- x2, y2 = pos[edge[1]]
550
+ # for idx, edge in enumerate(edges_cal):
551
+ for edge, weight in zip(list(graph.edge_list()), graph.edges()):
552
+ x1, y1 = qubit_positions[edge[0]]
553
+ x2, y2 = qubit_positions[edge[1]]
366
554
  x = (x1 + x2) / 2
367
555
  y = (y1 + y2) / 2
368
556
  plt.annotate(
369
- f"{edge[2]:.1e}",
557
+ f"{weight:.1e}",
370
558
  xy=(x, y),
371
559
  xytext=(0, 0),
372
560
  textcoords="offset points",
@@ -375,12 +563,68 @@ def plot_layout_fidelity_graph(
375
563
  bbox={"boxstyle": "round,pad=0.2", "fc": "white", "ec": "none", "alpha": 0.6},
376
564
  )
377
565
 
378
- plt.gca().invert_yaxis()
379
- plt.title(
380
- "Chip layout with selected qubits in orange\n"
381
- + "and gate errors indicated by edge thickness (thinner is better)"
566
+ # Add horizontal bar representing resonator
567
+ if topology == "star":
568
+ resonator_height = 3
569
+ resonator_thickness = 0.8
570
+ x_min = 0.5
571
+ x_max = qubit_positions[idx_to_qubit[qubit_nodes[-1]]][0] + 0.5
572
+ resonator_width = x_max - x_min
573
+
574
+ # Create rectangle with rounded corners
575
+ resonator = FancyBboxPatch(
576
+ (x_min, resonator_height - resonator_thickness / 2),
577
+ resonator_width,
578
+ resonator_thickness,
579
+ boxstyle="round,pad=0.01,rounding_size=0.3",
580
+ color="lightsteelblue",
581
+ zorder=10,
582
+ )
583
+ ax.add_patch(resonator)
584
+
585
+ # Add "Resonator" label in the center
586
+ plt.annotate(
587
+ "Resonator",
588
+ xy=((x_min + x_max) / 2, resonator_height),
589
+ xytext=(0, 0),
590
+ textcoords="offset points",
591
+ ha="center",
592
+ va="center",
593
+ color="black",
594
+ fontsize=10,
595
+ zorder=11,
596
+ bbox={"boxstyle": "round,pad=0.2", "fc": "white", "ec": "none", "alpha": 0.8},
597
+ )
598
+
599
+ # Calculate axis limits to ensure all circles are visible
600
+ all_x = [pos[0] for pos in qubit_positions.values()]
601
+ all_y = [pos[1] for pos in qubit_positions.values()]
602
+ x_min, x_max = min(all_x), max(all_x)
603
+ y_min, y_max = min(all_y), max(all_y)
604
+
605
+ # Add padding for circles
606
+ padding = max_radius * 1.5
607
+ ax.set_xlim(x_min - padding, x_max + padding)
608
+ ax.set_ylim(y_min - padding, y_max + padding)
609
+
610
+ # Adjust layout first
611
+ plt.tight_layout(pad=2.0)
612
+ ax.set_aspect("equal")
613
+ ax.invert_yaxis()
614
+
615
+ plt.figtext(
616
+ 0.5,
617
+ 0.99, # x=0.5 (center), y=0.01 (bottom)
618
+ f"Qubit connectivity with selected qubits in orange\n"
619
+ f"CZ errors -log(F) indicated by edge thickness (thinner is better)\n"
620
+ f"Single qubit errors -log(F) shown as node size with F computed from {sq_metric} metrics",
621
+ fontsize=10,
622
+ ha="center",
623
+ wrap=True,
382
624
  )
625
+
383
626
  plt.show()
627
+ return fig
384
628
 
385
629
 
386
630
  def rx_to_nx_graph(backend_coupling_map: CouplingMap) -> nx.Graph:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iqm-benchmarks
3
- Version: 2.43
3
+ Version: 2.45
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
@@ -63,7 +63,7 @@ Below is a list of the benchmarks currently available in the suite:
63
63
  * Gates / Layers:
64
64
  - Standard Clifford Randomized Benchmarking [[Phys. Rev. A 85, 042311](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.85.042311) (2012)]
65
65
  - Interleaved Randomized Benchmarking [[Phys. Rev. Lett. 109, 080505](https://doi.org/10.1103/PhysRevLett.109.080505) (2012)]
66
- - Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)]
66
+ - Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)] (Optional dependencies required)
67
67
  - Mirror Randomized Benchmarking [[Phys. Rev. Lett. 129, 150502](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.129.150502) (2022)]
68
68
  - Error Per Layered Gate [[arXiv:2311.05933 [quant-ph]](https://arxiv.org/abs/2311.05933) (2023)]
69
69
  * Holistic:
@@ -1,24 +1,24 @@
1
1
  iqm/benchmarks/__init__.py,sha256=-drZ_whue067Tu4I9RxBCqrrzu38Tm5Kqf9jHTftUPk,3070
2
2
  iqm/benchmarks/benchmark.py,sha256=3E7g7RQjCIGIpSI1gOSrI3V9SAVs-XEOMrPgToK_7vw,4972
3
- iqm/benchmarks/benchmark_definition.py,sha256=e4xe0wlWKZqj48_6-zTglMaMeoiA9aGkHrrSgoCfPkM,11271
3
+ iqm/benchmarks/benchmark_definition.py,sha256=7sq9_Y0iu1ZJOjG0gAVpm_BK3hbJBa6bdcRKhcVFOJw,11321
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=sItoMsfUYiMWTSCNOTe_RWi2l1xTf2slvXkFiEMRwKU,41091
8
- iqm/benchmarks/utils_plots.py,sha256=Q4h7gcKXf8Eizm13P0yL2I_P-QobHVFr9JCV83wrUi8,14942
7
+ iqm/benchmarks/utils.py,sha256=kJz9T9nJXpLl_iFYYUDtSq83N-Y3JFQBuvW1o7-AVSM,44137
8
+ iqm/benchmarks/utils_plots.py,sha256=CaqA9fJNgRnrbYqwBdpzFUlhwvKw5lhZX3KfRlroQV4,24420
9
9
  iqm/benchmarks/utils_shadows.py,sha256=e77PV_uaAO5m_woox9lAzompKAvFeDJ-0AKJrNJ7NFg,9728
10
10
  iqm/benchmarks/coherence/__init__.py,sha256=yeyhk-_Lp8IbJ-f5lQj0HP5Q1HSKK_FzuXHazotUrVY,704
11
11
  iqm/benchmarks/coherence/coherence.py,sha256=zX_6A8vCS2zeWesMDXPFZBfrJ8wUG90JI9_tFsonwXk,21191
12
12
  iqm/benchmarks/compressive_gst/__init__.py,sha256=LneifgYXtcwo2jcXo7GdUEHL6_peipukShhkrdaTRCA,929
13
- iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=2kiRttog4jR-vtMHu847GTFe5qL_i_uYr_4WMGqt9Ww,25653
14
- iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=g0kEovWbetoDRvX7JFrS9oOoNrqBxaFmprujJi7qQbU,36297
13
+ iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=c33PUisGlmPUkt4Nx2Xqmduji5qFCrWT9jMcoThcs7U,27992
14
+ iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=H6EQGbpI_sig69Jy6hflg6alMTtjB0t9tHftygzA2YA,41240
15
15
  iqm/benchmarks/entanglement/__init__.py,sha256=sHVVToRWRCz0LSntk1rQaoSNNeyZLPoiTjUKWZWrk1E,778
16
- iqm/benchmarks/entanglement/ghz.py,sha256=12bf9ANfgzyR7Vs8REO-Xm68gisqn8Q7_WSfaNnAmOk,41213
17
- iqm/benchmarks/entanglement/graph_states.py,sha256=7GMxuhbOeQXc3hn3yAwp51S-F-1qaP0AYXm6JtuL9gA,62560
16
+ iqm/benchmarks/entanglement/ghz.py,sha256=7QI7r9x5VEYQu5kqmdsLV9nq3eedYV_WRVqCQTbjapk,41294
17
+ iqm/benchmarks/entanglement/graph_states.py,sha256=saoAw2QF8j7W4nZMOElnjuNylqDAbY9cBwBypWZKUz8,62521
18
18
  iqm/benchmarks/optimization/__init__.py,sha256=_ajW_OibYLCtzU5AUv5c2zuuVYn8ZNeZUcUUSIGt51M,747
19
- iqm/benchmarks/optimization/qscore.py,sha256=D2BVVNAqO32uGu5_kLVl2XJUOBlRl1C-c6zYenaCBMg,37259
19
+ iqm/benchmarks/optimization/qscore.py,sha256=hh96ZVYU7rUYCBEQyhFZNv0IOQoAwM5RYfRvqRl05ow,37281
20
20
  iqm/benchmarks/quantum_volume/__init__.py,sha256=i-Q4SpDWELBw7frXnxm1j4wJRcxbIyrS5uEK_v06YHo,951
21
- iqm/benchmarks/quantum_volume/clops.py,sha256=fLY0aPHjNbW33SuVM9brAgBYFncDHjY5Bwh6iXzbjiU,31099
21
+ iqm/benchmarks/quantum_volume/clops.py,sha256=QS9iK-gtop0zix6IBeUumQeG01-0dXsv0jsYSDhgEu0,31071
22
22
  iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=pro7Lz-A5pPpT9UZ8wtXKTyhdWmTjQjRHt4BylDR-3U,36553
23
23
  iqm/benchmarks/randomized_benchmarking/__init__.py,sha256=IkKo-7zUChxZZd3my_csQCJfJfZNsV3-JTvdG8uqys4,734
24
24
  iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl,sha256=yrmSJqhv7Lb1yqiqU9-2baqTljJPNmTUPQR-AH6GGfc,7800
@@ -30,23 +30,23 @@ iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py,sha256=IGBrq_a
30
30
  iqm/benchmarks/randomized_benchmarking/direct_rb/__init__.py,sha256=lCIIeWMFZHnMUUEUTjUBvrhhUur6uBTHIVkxFBSfHC4,681
31
31
  iqm/benchmarks/randomized_benchmarking/direct_rb/direct_rb.py,sha256=Cbx6B9q8Sqc_uPalX6fUEWJX6UEHCfWRof9u4brtx5A,48977
32
32
  iqm/benchmarks/randomized_benchmarking/eplg/__init__.py,sha256=1MeGZTErElXJypQV2rQf7hwqLLvIp_JNVbwNhaP5vyI,696
33
- iqm/benchmarks/randomized_benchmarking/eplg/eplg.py,sha256=WZ3OwQ4C3K8SzudILAUwt71lb24vQQO9vaT0C9GCSOc,17215
33
+ iqm/benchmarks/randomized_benchmarking/eplg/eplg.py,sha256=3A_gxzAs6mi3APKvqCwYDcNwRogIZNy5SDL33Cro89E,17036
34
34
  iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py,sha256=sq6MgN_hwlpkOj10vyCU4e6eKSX-oLcF2L9na6W2Gt4,681
35
35
  iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=TaR1YFWBhOgm1hmEQzuwLYpp0yl0Xpuo3jAT6YhiXpc,28471
36
36
  iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=jRKbivWCZ3xdO1k0sx-ygC3s5DUkGSModd975PoAtcg,692
37
37
  iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=n_5gt9636ZDMsM9hC3Zm5qP2bQr2sy41zxGhOh0XMjI,32932
38
- iqm_benchmarks-2.43.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
38
+ iqm_benchmarks-2.45.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
39
39
  mGST/LICENSE,sha256=TtHNq55cUcbglb7uhVudeBLUh_qPdUoAEvU0BBwFz-k,1098
40
40
  mGST/README.md,sha256=v_5kw253csHF4-RfE-44KqFmBXIsSMRmOtN0AUPrRxE,5050
41
- mGST/additional_fns.py,sha256=_SEJ10FRNM7_CroysT8hCLZTfpm6ZhEIDCY5zPTnhjo,31390
42
- mGST/algorithm.py,sha256=mXcDG9_0j3hVjxLyc585dsj_yvydfrWNlCrcnUOQHPw,26625
41
+ mGST/additional_fns.py,sha256=MV0Pm5ap59IjhT_E3QhsZyM7lXOF1RZ9SD11zoaf43A,31781
42
+ mGST/algorithm.py,sha256=2xeIiPP1ae8IgBgrc7boTk-NyirUMJimcZTvKMbZqKY,27504
43
43
  mGST/compatibility.py,sha256=00DsPnNfOtrQcDTvxBDs-0aMhmuXmOIIxl_Ohy-Emkg,8920
44
- mGST/low_level_jit.py,sha256=uE1D3v01FbPpsbP92C4220OQalzOfxgL1Ku89BNkxLY,27377
45
- mGST/optimization.py,sha256=YHwkzIkYvsZOPjclR-BCQWh24jeqjuXp0BB0WX5Lwow,10559
46
- mGST/qiskit_interface.py,sha256=ajx6Zn5FnrX_T7tMP8xnBLyG4c2ddFRm0Fu2_3r1t30,10118
47
- mGST/reporting/figure_gen.py,sha256=6Xd8vwfy09hLY1YbJY6TRevuMsQSU4MsWqemly3ZO0I,12970
48
- mGST/reporting/reporting.py,sha256=B8NWfpZrrSmyH7lwZxd0EbZMYLsAGK1YsHRB4D5qXH4,26002
49
- iqm_benchmarks-2.43.dist-info/METADATA,sha256=ezkJGO1628fD-X8lXDqtZUeEK9y7kIsfZjDw-JqZBzs,10872
50
- iqm_benchmarks-2.43.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- iqm_benchmarks-2.43.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
52
- iqm_benchmarks-2.43.dist-info/RECORD,,
44
+ mGST/low_level_jit.py,sha256=Ih1MxcwU0GnSRu0TI9GaYxpB94CMuJ8Is1eTeOwsfA8,30772
45
+ mGST/optimization.py,sha256=x9tJ9wMQ5aONWpNpBMVtK0rwE6DRcOU33htNgrt0tx4,11015
46
+ mGST/qiskit_interface.py,sha256=uCdn-Q9CXI2f4FQSxGUy8GmmzQhr9NhCOFb2VPj0gTs,10061
47
+ mGST/reporting/figure_gen.py,sha256=xFPAHx1Trdqz7swn0kRqwc_jbRaNxhG9Nvx0jeitooo,25847
48
+ mGST/reporting/reporting.py,sha256=Wss1-zFsMEhzrrXKfP-RICau80ezjDIzcN555KhSehc,34160
49
+ iqm_benchmarks-2.45.dist-info/METADATA,sha256=a121AuXxTW_ziKGmIdEd-qjjBunWeV3iFEp60U4kT9o,10905
50
+ iqm_benchmarks-2.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
+ iqm_benchmarks-2.45.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
52
+ iqm_benchmarks-2.45.dist-info/RECORD,,
mGST/additional_fns.py CHANGED
@@ -2,8 +2,10 @@
2
2
  Utility functions used by mGST modules
3
3
  """
4
4
 
5
+ import contextlib
5
6
  import os
6
7
  import random
8
+ import sys
7
9
  import warnings
8
10
 
9
11
  import numpy as np
@@ -13,6 +15,18 @@ from scipy.linalg import expm, qr
13
15
  from mGST.low_level_jit import Mp_norm_lower, MVE_lower, contract, local_basis
14
16
 
15
17
 
18
+ @contextlib.contextmanager
19
+ def suppress_stdout():
20
+ """Context manager to temporarily suppress stdout output."""
21
+ with open(os.devnull, "w", encoding="utf-8") as devnull:
22
+ old_stdout = sys.stdout
23
+ sys.stdout = devnull
24
+ try:
25
+ yield
26
+ finally:
27
+ sys.stdout = old_stdout
28
+
29
+
16
30
  def transp(dim1, dim2):
17
31
  """Superoperator of a map that performs the transpose operation
18
32
 
@@ -261,6 +275,10 @@ def random_gs(d, r, rK, n_povm):
261
275
  The second axis enumerates Kraus operators for a gate specified by the first axis.
262
276
  X: 3D numpy array
263
277
  Array where random CPT superoperators are stacked along the first axis.
278
+ E: 2D numpy array
279
+ Randomly generated POVM elements stacked along the first axis.
280
+ rho: 1D numpy array
281
+ Random initial state vector
264
282
 
265
283
  Notes:
266
284
  The Kraus operators are generated from random unitaries, see function randKrausSet
@@ -327,7 +345,7 @@ def perturbed_target_init(X_target, rK):
327
345
  """
328
346
  d, r, _ = X_target.shape
329
347
  pdim = int(np.sqrt(r))
330
- K_perturb = randKrausSet(d, r, rK, a=0.1)
348
+ K_perturb = randKrausSet(d, r, rK, a=0.05)
331
349
  X_perturb = np.einsum("ijkl,ijnm -> iknlm", K_perturb, K_perturb.conj()).reshape((d, r, r))
332
350
  X_init = np.einsum("ikl,ilm ->ikm", X_perturb, X_target)
333
351
  K_init = Kraus_rep(X_init, d, pdim, rK)
@@ -627,8 +645,7 @@ def sampled_measurements(y, n):
627
645
  y_new = np.maximum(np.minimum(y, 1), 0)
628
646
  if np.sum(np.abs(y_new - y)) > 1e-6:
629
647
  warnings.warn(
630
- f"Warning: Probabilities capped to interval [0,1]",
631
- f"l1-difference to input:%f" % np.sum(np.abs(y_new - y)),
648
+ f"Warning: Probabilities capped to interval [0,1], l1-difference to input: {np.sum(np.abs(y_new - y))}"
632
649
  )
633
650
  y = y_new
634
651
  rng = np.random.default_rng()
@@ -723,24 +740,22 @@ def is_positive(X, E, rho):
723
740
  eigvals = np.array([la.eigvals(X_choi[i]) for i in range(d)])
724
741
  partial_traces = np.einsum("aiikl -> akl", X.reshape(d, pdim, pdim, pdim, pdim))
725
742
  povm_eigvals = np.array([la.eigvals(E[i].reshape(pdim, pdim)) for i in range(n_povm)])
726
- if np.any(np.imag(eigvals.reshape(-1) > 1e-10)):
727
- print("Gates are not all hermitian.")
728
- else:
729
- for i in range(d):
730
- print(f"Gate %i positive:" % i, np.all(eigvals[i, :] > -1e-10))
731
- print(f"Gate %i trace preserving:" % i, la.norm(partial_traces[i] - np.eye(pdim)) < 1e-10)
732
- print(f"Initial state positive:", np.all(la.eigvals(rho.reshape(pdim, pdim)) > -1e-10))
733
- print(f"Initial state normalization:", np.trace(rho.reshape(pdim, pdim)))
734
- print(
735
- "fPOVM valid:",
736
- np.all(
737
- [
738
- la.norm(np.sum(E, axis=0).reshape(pdim, pdim) - np.eye(pdim)) < 1e-10,
739
- np.all(povm_eigvals.reshape(-1) > -1e-10),
740
- ]
741
- ),
742
- )
743
743
 
744
+ # Check if gates are hermitian
745
+ assert np.all(np.imag(eigvals.reshape(-1)) < 1e-10)
746
+
747
+ # Check if gates are positive and trace preserving
748
+ for i in range(d):
749
+ assert np.all(eigvals[i, :] > -1e-10)
750
+ assert la.norm(partial_traces[i] - np.eye(pdim)) < 1e-10
751
+
752
+ # Initial state positivity and normalization
753
+ assert np.all(la.eigvals(rho.reshape(pdim, pdim)) > -1e-10)
754
+ assert np.abs(np.trace(rho.reshape(pdim, pdim))) - 1 < 1e-10
755
+
756
+ # POVM positivity and normalization
757
+ assert la.norm(np.sum(E, axis=0).reshape(pdim, pdim) - np.eye(pdim)) < 1e-6
758
+ assert np.all(povm_eigvals.reshape(-1) > -1e-10)
744
759
 
745
760
  def tvd(X, E, rho, J, y_data):
746
761
  """Return the total variation distance between model probabilities for the circuits in J