iqm-benchmarks 1.6__py3-none-any.whl → 1.8__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.

@@ -19,14 +19,18 @@ GHZ state benchmark
19
19
  from io import BytesIO
20
20
  from itertools import chain
21
21
  import json
22
- from time import strftime, time
23
- from typing import Dict, List, Optional, Tuple, Type, cast
22
+ from time import strftime
23
+ from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, cast
24
24
 
25
+ from matplotlib.figure import Figure
26
+ import matplotlib.pyplot as plt
27
+ import networkx
25
28
  from networkx import Graph, all_pairs_shortest_path, is_connected, minimum_spanning_tree
26
29
  import numpy as np
27
30
  import pycurl
28
31
  from qiskit import QuantumCircuit, transpile
29
32
  from qiskit.quantum_info import random_clifford
33
+ from qiskit.transpiler import CouplingMap
30
34
  from qiskit_aer import Aer
31
35
  from scipy.spatial.distance import hamming
32
36
  import xarray as xr
@@ -45,7 +49,9 @@ from iqm.benchmarks.readout_mitigation import apply_readout_error_mitigation
45
49
  from iqm.benchmarks.utils import (
46
50
  perform_backend_transpilation,
47
51
  reduce_to_active_qubits,
52
+ retrieve_all_counts,
48
53
  set_coupling_map,
54
+ submit_execute,
49
55
  timeit,
50
56
  xrvariable_to_counts,
51
57
  )
@@ -68,16 +74,17 @@ def append_rms(
68
74
  Returns:
69
75
  List[QuantumCircuit] of the original circuit with 1Q Clifford gates appended to it
70
76
  """
71
- rm_circuits = []
77
+ rm_circuits: list[QuantumCircuit] = []
72
78
  for _ in range(num_rms):
73
79
  rm_circ = circuit.copy()
74
80
  # It shouldn't matter if measurement bits get scrambled
75
81
  rm_circ.remove_final_measurements()
76
82
 
77
83
  active_qubits = set()
78
- for instruction in rm_circ.data:
84
+ data = rm_circ.data
85
+ for instruction in data:
79
86
  for qubit in instruction[1]:
80
- active_qubits.add(qubit.index)
87
+ active_qubits.add(rm_circ.find_bit(qubit)[0])
81
88
 
82
89
  for q in active_qubits:
83
90
  if backend is not None:
@@ -97,7 +104,7 @@ def append_rms(
97
104
 
98
105
  def fidelity_ghz_randomized_measurements(
99
106
  dataset: xr.Dataset, qubit_layout, ideal_probabilities: List[Dict[str, int]], num_qubits: int
100
- ) -> Dict[str, float]:
107
+ ) -> tuple[dict[str, Any], dict[str, Any]]:
101
108
  """
102
109
  Estimates GHZ state fidelity through cross-correlations of RMs.
103
110
  Implementation of Eq. (34) in https://arxiv.org/abs/1812.02624
@@ -108,21 +115,24 @@ def fidelity_ghz_randomized_measurements(
108
115
  ideal_probabilities (List[Dict[str, int]]):
109
116
  num_qubits (int):
110
117
  Returns:
111
- Dict[str, float]
118
+ values: dict[str, Any]
119
+ The fidelities
120
+ uncertainties: dict[str, Any]
121
+ The uncertainties for the fidelities
112
122
  """
113
-
123
+ idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
114
124
  # List for each RM contribution to the fidelity
115
125
  fid_rm = []
116
126
 
117
127
  # Loop through RMs and add each contribution
118
- num_rms = len(dataset.attrs["transpiled_circuits"][f"{str(qubit_layout)}"][tuple(qubit_layout)])
128
+ num_rms = len(dataset.attrs["transpiled_circuits"][f"{idx}"])
119
129
  for u in range(num_rms):
120
130
  # Probability estimates for noisy measurements
121
131
  probabilities_sample = {}
122
- c_keys = dataset[f"{str(qubit_layout)}_state_{u}"].data # measurements[u].keys()
123
- num_shots_noisy = sum(dataset[f"{str(qubit_layout)}_counts_{u}"].data)
132
+ c_keys = dataset[f"{idx}_state_{u}"].data # measurements[u].keys()
133
+ num_shots_noisy = sum(dataset[f"{idx}_counts_{u}"].data)
124
134
  for k, key in enumerate(c_keys):
125
- probabilities_sample[key] = dataset[f"{str(qubit_layout)}_counts_{u}"].data[k] / num_shots_noisy
135
+ probabilities_sample[key] = dataset[f"{idx}_counts_{u}"].data[k] / num_shots_noisy
126
136
  # Keys for corresponding ideal probabilities
127
137
  c_id_keys = ideal_probabilities[u].keys()
128
138
 
@@ -132,17 +142,18 @@ def fidelity_ghz_randomized_measurements(
132
142
  exponent = hamming(list(sa), list(sb)) * num_qubits
133
143
  p_sum.append(np.power(-2, -exponent) * probabilities_sample[sa] * ideal_probabilities[u][sb])
134
144
  fid_rm.append((2**num_qubits) * sum(p_sum))
135
- fidelities = {"mean": np.mean(fid_rm), "std": np.std(fid_rm) / np.sqrt(num_rms)}
145
+ values = {"fidelity": np.mean(fid_rm)}
146
+ uncertainties = {"fidelity": np.std(fid_rm) / np.sqrt(num_rms)}
136
147
 
137
148
  if dataset.attrs["rem"]:
138
149
  fid_rm_rem = []
139
150
  for u in range(num_rms):
140
151
  # Probability estimates for noisy measurements
141
152
  probabilities_sample = {}
142
- c_keys = dataset[f"{str(qubit_layout)}_rem_state_{u}"].data # measurements[u].keys()
143
- num_shots_noisy = sum(dataset[f"{str(qubit_layout)}_rem_counts_{u}"].data)
153
+ c_keys = dataset[f"{idx}_rem_state_{u}"].data # measurements[u].keys()
154
+ num_shots_noisy = sum(dataset[f"{idx}_rem_counts_{u}"].data)
144
155
  for k, key in enumerate(c_keys):
145
- probabilities_sample[key] = dataset[f"{str(qubit_layout)}_rem_counts_{u}"].data[k] / num_shots_noisy
156
+ probabilities_sample[key] = dataset[f"{idx}_rem_counts_{u}"].data[k] / num_shots_noisy
146
157
  # Keys for corresponding ideal probabilities
147
158
  c_id_keys = ideal_probabilities[u].keys()
148
159
 
@@ -152,11 +163,12 @@ def fidelity_ghz_randomized_measurements(
152
163
  exponent = hamming(list(sa), list(sb)) * num_qubits
153
164
  p_sum.append(np.power(-2, -exponent) * probabilities_sample[sa] * ideal_probabilities[u][sb])
154
165
  fid_rm_rem.append((2**num_qubits) * sum(p_sum))
155
- fidelities = fidelities | {"mean_rem": np.mean(fid_rm_rem), "std_rem": np.std(fid_rm_rem) / np.sqrt(num_rms)}
156
- return fidelities
166
+ values = values | {"fidelity_rem": np.mean(fid_rm_rem)}
167
+ uncertainties = uncertainties | {"fidelity_rem": np.std(fid_rm_rem) / np.sqrt(num_rms)}
168
+ return values, uncertainties
157
169
 
158
170
 
159
- def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> List[float]:
171
+ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> list[Any]:
160
172
  """
161
173
  Estimates the GHZ state fidelity based on the multiple quantum coherences method based on [Mooney, 2021]
162
174
 
@@ -167,12 +179,13 @@ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> Lis
167
179
  The subset of system-qubits used in the protocol
168
180
 
169
181
  Returns:
170
- List[int]: The ghz fidelity or, if rem=True, fidelity and readout error mitigated fidelity
182
+ dict[str, dict[str, Any]]: The ghz fidelity or, if rem=True, fidelity and readout error mitigated fidelity
171
183
  """
172
184
 
173
185
  num_qubits = len(qubit_layout)
174
186
  phases = [np.pi * i / (num_qubits + 1) for i in range(2 * num_qubits + 2)]
175
- transpiled_circuits = dataset.attrs["transpiled_circuits"][f"{str(qubit_layout)}"][tuple(qubit_layout)]
187
+ idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
188
+ transpiled_circuits = dataset.attrs["transpiled_circuits"][idx]
176
189
  num_shots = dataset.attrs["shots"]
177
190
  num_circuits = len(transpiled_circuits)
178
191
 
@@ -180,16 +193,7 @@ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> Lis
180
193
  complex_coefficients = np.exp(1j * num_qubits * np.array(phases))
181
194
 
182
195
  # Loading the counts from the dataset
183
- counts = xrvariable_to_counts(dataset, str(qubit_layout), num_circuits)
184
- # for u in range(num_circuits):
185
- # counts.append(
186
- # dict(
187
- # zip(
188
- # list(dataset[f"{str(qubit_layout)}_state_{u}"].data),
189
- # dataset[f"{str(qubit_layout)}_counts_{u}"].data,
190
- # )
191
- # )
192
- # )
196
+ counts = xrvariable_to_counts(dataset, f"{idx}", num_circuits)
193
197
  all_zero_probability_list = [] # An ordered list for storing the probabilities of returning to the |00..0> state
194
198
  for count in counts[1:]:
195
199
  if "0" * num_qubits in count.keys():
@@ -211,16 +215,7 @@ def fidelity_ghz_coherences(dataset: xr.Dataset, qubit_layout: List[int]) -> Lis
211
215
 
212
216
  # Same procedure for error mitigated data
213
217
  if dataset.attrs["rem"]:
214
- probs_mit = xrvariable_to_counts(dataset, f"{str(qubit_layout)}_rem", num_circuits)
215
- # for u in range(num_circuits):
216
- # probs_mit.append(
217
- # dict(
218
- # zip(
219
- # list(dataset[f"{str(qubit_layout)}_rem_state_{u}"].data),
220
- # dataset[f"{str(qubit_layout)}_rem_counts_{u}"].data,
221
- # )
222
- # )
223
- # )
218
+ probs_mit = xrvariable_to_counts(dataset, f"{idx}_rem", num_circuits)
224
219
  all_zero_probability_list_mit = []
225
220
  for prob in probs_mit[1:]:
226
221
  if "0" * num_qubits in prob.keys():
@@ -257,26 +252,28 @@ def fidelity_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
257
252
  for qubit_layout in qubit_layouts:
258
253
  if routine == "randomized_measurements":
259
254
  ideal_simulator = Aer.get_backend("statevector_simulator")
260
- for qubit_layout in qubit_layouts:
261
- ideal_probabilities = []
262
- all_circuits = run.dataset.attrs["transpiled_circuits"][str(qubit_layout)][tuple(qubit_layout)]
263
- for qc in all_circuits:
264
- qc_copy = qc.copy()
265
- qc_copy.remove_final_measurements()
266
- deflated_qc = reduce_to_active_qubits(qc_copy, backend_name)
267
- ideal_probabilities.append(
268
- dict(sorted(ideal_simulator.run(deflated_qc).result().get_counts().items()))
255
+ ideal_probabilities = []
256
+ idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
257
+ all_circuits = run.dataset.attrs["transpiled_circuits"][idx]
258
+ for qc in all_circuits:
259
+ qc_copy = qc.copy()
260
+ qc_copy.remove_final_measurements()
261
+ deflated_qc = reduce_to_active_qubits(qc_copy, backend_name)
262
+ ideal_probabilities.append(dict(sorted(ideal_simulator.run(deflated_qc).result().get_counts().items())))
263
+ values, uncertainties = fidelity_ghz_randomized_measurements(
264
+ dataset, qubit_layout, ideal_probabilities, len(qubit_layout)
265
+ )
266
+ observation_list.extend(
267
+ [
268
+ BenchmarkObservation(
269
+ name=key,
270
+ identifier=BenchmarkObservationIdentifier(qubit_layout),
271
+ value=value,
272
+ uncertainty=uncertainties[key],
269
273
  )
270
- observation_list.extend(
271
- [
272
- BenchmarkObservation(
273
- name=key, identifier=BenchmarkObservationIdentifier(qubit_layout), value=value
274
- )
275
- for key, value in fidelity_ghz_randomized_measurements(
276
- dataset, qubit_layout, ideal_probabilities, len(qubit_layout)
277
- ).items()
278
- ]
279
- )
274
+ for key, value in values.items()
275
+ ]
276
+ )
280
277
  else: # default routine == "coherences":
281
278
  fidelity = fidelity_ghz_coherences(dataset, qubit_layout)
282
279
  observation_list.extend(
@@ -287,13 +284,13 @@ def fidelity_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
287
284
  ]
288
285
  )
289
286
  if len(fidelity) > 1:
290
-
291
287
  observation_list.append(
292
288
  BenchmarkObservation(
293
289
  name="fidelity_rem", identifier=BenchmarkObservationIdentifier(qubit_layout), value=fidelity[1]
294
290
  )
295
291
  )
296
- return BenchmarkAnalysisResult(dataset=dataset, observations=observation_list)
292
+ plots = {"All layout fidelities": plot_fidelities(observation_list, qubit_layouts)}
293
+ return BenchmarkAnalysisResult(dataset=dataset, observations=observation_list, plots=plots)
297
294
 
298
295
 
299
296
  def generate_ghz_linear(num_qubits: int) -> QuantumCircuit:
@@ -442,18 +439,23 @@ def extract_fidelities(cal_url: str, qubit_layout: List[int]) -> Tuple[List[List
442
439
  return list_couplings, list_fids
443
440
 
444
441
 
445
- def get_edges(coupling_map, qubit_layout, edges_cal=None, fidelities_cal=None):
442
+ def get_edges(
443
+ coupling_map: CouplingMap,
444
+ qubit_layout: List[int],
445
+ edges_cal: Optional[List[List[int]]] = None,
446
+ fidelities_cal: Optional[List[float]] = None,
447
+ ):
446
448
  """Produces a networkx.Graph from coupling map fidelity information, with edges given by couplings
447
449
  and edge weights given by fidelities
448
450
 
449
451
  Args:
450
- coupling_map: List[int]
452
+ coupling_map (CouplingMap):
451
453
  The list pairs on which 2-qubit gates are natively supported
452
- qubit_layout: List[int]
454
+ qubit_layout (List[int]):
453
455
  The subset of system-qubits used in the protocol, indexed from 0
454
- edges_cal: List[int]
455
- Same as the coupling map, but only with connections that have CZ fidelities in the calibration data
456
- fidelities_cal: List[float]
456
+ edges_cal (Optional[List[List[int]]]):
457
+ A coupling map of qubit pairs that have CZ fidelities in the calibration data
458
+ fidelities_cal (Optional[List[float]]):
457
459
  A list of CZ fidelities ordered in the same way as edges_cal
458
460
 
459
461
  Returns:
@@ -466,16 +468,18 @@ def get_edges(coupling_map, qubit_layout, edges_cal=None, fidelities_cal=None):
466
468
  if edge[0] in qubit_layout and edge[1] in qubit_layout:
467
469
  edges_patch.append([edge[0], edge[1]])
468
470
 
469
- if fidelities_cal is None:
470
- weights = np.ones(len(edges_patch))
471
- else:
472
- fidelities_cal = np.minimum(np.array(fidelities_cal), np.ones(len(fidelities_cal))) # get rid of > 1 fidelities
471
+ if fidelities_cal is not None:
472
+ fidelities_cal = list(
473
+ np.minimum(np.array(fidelities_cal), np.ones(len(fidelities_cal)))
474
+ ) # get rid of > 1 fidelities
473
475
  fidelities_patch = []
474
476
  for edge in edges_patch:
475
- for idx, edge_2 in enumerate(edges_cal):
477
+ for idx, edge_2 in enumerate(cast(List[int], edges_cal)):
476
478
  if edge == edge_2:
477
479
  fidelities_patch.append(fidelities_cal[idx])
478
480
  weights = -np.log(np.array(fidelities_patch))
481
+ else:
482
+ weights = np.ones(len(edges_patch))
479
483
  graph = Graph()
480
484
  for idx, edge in enumerate(edges_patch):
481
485
  graph.add_edge(*edge, weight=weights[idx])
@@ -484,7 +488,7 @@ def get_edges(coupling_map, qubit_layout, edges_cal=None, fidelities_cal=None):
484
488
  return graph
485
489
 
486
490
 
487
- def get_cx_map(qubit_layout, graph) -> list[list[int]]:
491
+ def get_cx_map(qubit_layout: List[int], graph: networkx.Graph) -> list[list[int]]:
488
492
  """Calculate the cx_map based on participating qubits and the 2QB gate fidelities between them.
489
493
 
490
494
  Uses networkx graph algorithms to calculate the minimal spanning tree of the subgraph defined by qubit_layout.
@@ -525,8 +529,56 @@ def get_cx_map(qubit_layout, graph) -> list[list[int]]:
525
529
  return cx_map
526
530
 
527
531
 
532
+ def plot_fidelities(observations: List[BenchmarkObservation], qubit_layouts: List[List[int]]) -> Figure:
533
+ """Plots all the fidelities stored in the observations into a single plot of fidelity vs. number of qubits
534
+
535
+ Parameters
536
+ ----------
537
+ observations: List[BenchmarkObservation]
538
+ A list of Observations, each assumed to be a fidelity
539
+ qubit_layouts
540
+ The list of qubit layouts as given by the user. This is used to name the layouts in order for identification
541
+ in the plot.
542
+ Returns
543
+ -------
544
+ fig :Figure
545
+ The figure object with the fidelity plot.
546
+ """
547
+ fig, ax = plt.subplots()
548
+ layout_short = {str(qubit_layout): f" L{i}" for i, qubit_layout in enumerate(qubit_layouts)}
549
+ recorded_labels = []
550
+ for i, obs in enumerate(observations):
551
+ label = "With REM" if "rem" in obs.name else "Unmitigated"
552
+ if label in recorded_labels:
553
+ label = "_nolegend_"
554
+ else:
555
+ recorded_labels.append(label)
556
+ x = sum(c.isdigit() for c in obs.identifier.string_identifier)
557
+ y = obs.value
558
+ ax.errorbar(
559
+ x,
560
+ y,
561
+ yerr=obs.uncertainty,
562
+ capsize=4,
563
+ color="orange" if "rem" in obs.name else "cornflowerblue",
564
+ label=label,
565
+ fmt="o",
566
+ alpha=1,
567
+ markersize=5,
568
+ )
569
+ ax.annotate(layout_short[obs.identifier.string_identifier], (x, y))
570
+ ax.axhline(0.5, linestyle="--", color="black", label="GME threshold")
571
+ # ax.set_ylim([0,1])
572
+ ax.set_title("GHZ fidelities of all qubit layouts")
573
+ ax.set_xlabel("Number of qubits")
574
+ ax.set_ylabel("Fidelity")
575
+ ax.legend(framealpha=0.5)
576
+ plt.close()
577
+ return fig
578
+
579
+
528
580
  class GHZBenchmark(Benchmark):
529
- """The GHZ Benchmark estimates the quality of generated Greenberger-Horne-Zeilinger states"""
581
+ """The GHZ Benchmark estimates the quality of generated GreenbergerHorneZeilinger states"""
530
582
 
531
583
  analysis_function = staticmethod(fidelity_analysis)
532
584
  name = "ghz"
@@ -541,28 +593,27 @@ class GHZBenchmark(Benchmark):
541
593
  super().__init__(backend, configuration)
542
594
 
543
595
  self.state_generation_routine = configuration.state_generation_routine
544
- self.choose_qubits_routine = configuration.choose_qubits_routine
596
+ # self.choose_qubits_routine = configuration.choose_qubits_routine
545
597
  if configuration.custom_qubits_array:
546
598
  self.custom_qubits_array = configuration.custom_qubits_array
547
599
  else:
548
- self.custom_qubits_array = list(set(chain(*backend.coupling_map)))
600
+ self.custom_qubits_array = [list(set(chain(*backend.coupling_map)))]
601
+ self.qubit_counts: Sequence[int] | List[int]
549
602
  if not configuration.qubit_counts:
550
603
  self.qubit_counts = [len(layout) for layout in self.custom_qubits_array]
551
604
  else:
552
605
  if any(np.max(configuration.qubit_counts) > [len(layout) for layout in self.custom_qubits_array]):
553
606
  raise ValueError("The maximum given qubit count is larger than the size of the smallest qubit layout.")
554
607
  self.qubit_counts = configuration.qubit_counts
608
+ # self.layout_idx_mapping = {str(qubit_layout): idx for idx, qubit_layout in enumerate(self.custom_qubits_array)}
555
609
 
556
610
  self.qiskit_optim_level = configuration.qiskit_optim_level
557
611
  self.optimize_sqg = configuration.optimize_sqg
558
-
559
612
  self.fidelity_routine = configuration.fidelity_routine
560
613
  self.num_RMs = configuration.num_RMs
561
-
562
614
  self.rem = configuration.rem
563
615
  self.mit_shots = configuration.mit_shots
564
616
  self.cal_url = configuration.cal_url
565
-
566
617
  self.timestamp = strftime("%Y%m%d-%H%M%S")
567
618
 
568
619
  # @staticmethod
@@ -587,10 +638,11 @@ class GHZBenchmark(Benchmark):
587
638
  """
588
639
  # num_qubits = len(qubit_layout)
589
640
  fixed_coupling_map = set_coupling_map(qubit_layout, self.backend, "fixed")
590
-
641
+ idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
642
+ ghz_native_transpiled: List[QuantumCircuit]
591
643
  if routine == "naive":
592
644
  ghz = generate_ghz_linear(qubit_count)
593
- self.untranspiled_circuits[str(qubit_layout)].update({qubit_count: ghz})
645
+ self.untranspiled_circuits[idx].update({qubit_count: ghz})
594
646
  ghz_native_transpiled, _ = perform_backend_transpilation(
595
647
  [ghz],
596
648
  self.backend,
@@ -607,7 +659,7 @@ class GHZBenchmark(Benchmark):
607
659
  else:
608
660
  graph = get_edges(self.backend.coupling_map, qubit_layout)
609
661
  ghz, _ = generate_ghz_spanning_tree(graph, qubit_layout, qubit_count)
610
- self.untranspiled_circuits[str(qubit_layout)].update({qubit_count: ghz})
662
+ self.untranspiled_circuits[idx].update({qubit_count: ghz})
611
663
  ghz_native_transpiled, _ = perform_backend_transpilation(
612
664
  [ghz],
613
665
  self.backend,
@@ -631,11 +683,11 @@ class GHZBenchmark(Benchmark):
631
683
  if ghz_native_transpiled[0].depth() == ghz_native_transpiled[1].depth():
632
684
  index_min_2q = np.argmin([c.count_ops()["cz"] for c in ghz_native_transpiled])
633
685
  final_ghz = ghz_native_transpiled[index_min_2q]
634
- self.untranspiled_circuits[str(qubit_layout)].update({qubit_count: ghz_log[index_min_2q]})
686
+ self.untranspiled_circuits[idx].update({qubit_count: ghz_log[index_min_2q]})
635
687
  else:
636
688
  index_min_depth = np.argmin([c.depth() for c in ghz_native_transpiled])
637
689
  final_ghz = ghz_native_transpiled[index_min_depth]
638
- self.untranspiled_circuits[str(qubit_layout)].update({qubit_count: ghz_log[index_min_depth]})
690
+ self.untranspiled_circuits[idx].update({qubit_count: ghz_log[index_min_depth]})
639
691
  return final_ghz[0]
640
692
 
641
693
  def generate_coherence_meas_circuits(self, qubit_layout: List[int], qubit_count: int) -> List[QuantumCircuit]:
@@ -653,7 +705,8 @@ class GHZBenchmark(Benchmark):
653
705
  A list of transpiled quantum circuits to be measured
654
706
  """
655
707
 
656
- qc = self.untranspiled_circuits[str(qubit_layout)][qubit_count]
708
+ idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
709
+ qc = self.untranspiled_circuits[idx][qubit_count]
657
710
  qc_list = [qc.copy()]
658
711
 
659
712
  qc.remove_final_measurements()
@@ -678,7 +731,7 @@ class GHZBenchmark(Benchmark):
678
731
  qiskit_optim_level=self.qiskit_optim_level,
679
732
  optimize_sqg=self.optimize_sqg,
680
733
  )
681
- self.untranspiled_circuits[str(qubit_layout)].update({qubit_count: qc_list})
734
+ self.untranspiled_circuits[idx].update({qubit_count: qc_list})
682
735
  return qc_list_transpiled
683
736
 
684
737
  def generate_readout_circuit(self, qubit_layout, qubit_count):
@@ -697,33 +750,31 @@ class GHZBenchmark(Benchmark):
697
750
  A list of transpiled quantum circuits to be measured
698
751
  """
699
752
  # Generate the list of circuits
700
- self.untranspiled_circuits[str(qubit_layout)] = {}
701
- self.transpiled_circuits[str(qubit_layout)] = {}
753
+ idx = BenchmarkObservationIdentifier(qubit_layout).string_identifier
754
+ self.untranspiled_circuits[idx] = {}
755
+ self.transpiled_circuits[idx] = {}
702
756
 
703
757
  qcvv_logger.info(f"Now generating a {len(qubit_layout)}-qubit GHZ state on qubits {qubit_layout}")
704
758
  transpiled_ghz = self.generate_native_ghz(qubit_layout, qubit_count, self.state_generation_routine)
705
759
 
706
760
  if self.fidelity_routine == "randomized_measurements":
707
761
  all_circuits_list, _ = append_rms(transpiled_ghz, cast(int, self.num_RMs), self.backend)
708
- all_circuits_dict = {tuple(qubit_layout): all_circuits_list}
709
762
  elif self.fidelity_routine == "coherences":
710
763
  all_circuits_list = self.generate_coherence_meas_circuits(qubit_layout, qubit_count)
711
- all_circuits_dict = {tuple(qubit_layout): all_circuits_list}
712
764
  else:
713
765
  all_circuits_list = transpiled_ghz
714
- all_circuits_dict = {tuple(qubit_layout): all_circuits_list}
715
766
 
716
- self.transpiled_circuits[str(qubit_layout)].update(all_circuits_dict)
767
+ self.transpiled_circuits.update({idx: all_circuits_list})
717
768
  return all_circuits_list
718
769
 
719
- def add_configuration_to_dataset(self, dataset): # CHECK
770
+ def add_configuration_to_dataset(self, dataset: xr.Dataset): # CHECK
720
771
  """
721
- Creates an xarray.Dataset and adds the circuits and configuration metadata to it
772
+ Creates a xarray.Dataset and adds the circuits and configuration metadata to it.
722
773
 
723
774
  Args:
724
- self: Source class
775
+ dataset (xr.Dataset):
725
776
  Returns:
726
- dataset: xarray.Dataset to be used for further data storage
777
+ xr.Dataset: dataset to be used for further data storage
727
778
  """
728
779
 
729
780
  for key, value in self.configuration:
@@ -742,28 +793,38 @@ class GHZBenchmark(Benchmark):
742
793
  aux_custom_qubits_array = cast(List[List[int]], self.custom_qubits_array).copy()
743
794
  dataset = xr.Dataset()
744
795
 
796
+ # Submit all
797
+ all_jobs: Dict = {}
745
798
  for qubit_layout in aux_custom_qubits_array:
799
+ Id = BenchmarkObservationIdentifier(qubit_layout)
800
+ idx = Id.string_identifier
801
+ # for qubit_count in self.qubit_counts[idx]:
746
802
  qubit_count = len(qubit_layout)
747
803
  circuits = self.generate_readout_circuit(qubit_layout, qubit_count)
748
-
749
- qcvv_logger.info(f"Retrieving results")
750
- t_start = time()
751
- job = backend.run(circuits, shots=self.shots)
752
- counts = job.result().get_counts()
753
- print(f"\t Getting counts took {time()-t_start:.2f} sec")
754
-
755
- # coordinates = [(f"qubit_layout", [str(qubit_layout)])]
756
- identifier = str(qubit_layout)
757
- qcvv_logger.info(f"Adding counts to dataset")
758
- dataset, _ = add_counts_to_dataset(counts, identifier, dataset)
804
+ transpiled_circuit_dict = {tuple(qubit_layout): circuits}
805
+ all_jobs[idx], _ = submit_execute(
806
+ transpiled_circuit_dict,
807
+ backend,
808
+ self.shots,
809
+ self.calset_id,
810
+ max_gates_per_batch=self.max_gates_per_batch,
811
+ )
812
+ # Retrieve all
813
+ qcvv_logger.info(f"Retrieving counts and adding counts to dataset...")
814
+ for qubit_layout in aux_custom_qubits_array:
815
+ # for qubit_count in self.qubit_counts[idx]:
816
+ Id = BenchmarkObservationIdentifier(qubit_layout)
817
+ idx = Id.string_identifier
818
+ qubit_count = len(qubit_layout)
819
+ counts, _ = retrieve_all_counts(all_jobs[idx])
820
+ dataset, _ = add_counts_to_dataset(counts, idx, dataset)
759
821
  if self.rem:
760
822
  qcvv_logger.info(f"Applying readout error mitigation")
761
- rem_results, _ = apply_readout_error_mitigation(
762
- backend, circuits, job.result().get_counts(), self.mit_shots
763
- )
823
+ circuits = self.transpiled_circuits[idx]
824
+ rem_results, _ = apply_readout_error_mitigation(backend, circuits, counts, self.mit_shots)
764
825
  rem_results_dist = [counts_mit.nearest_probability_distribution() for counts_mit in rem_results]
765
- qcvv_logger.info(f"Adding REM results to dataset")
766
- dataset, _ = add_counts_to_dataset(rem_results_dist, f"{identifier}_rem", dataset)
826
+ dataset, _ = add_counts_to_dataset(rem_results_dist, f"{idx}_rem", dataset)
827
+
767
828
  self.add_configuration_to_dataset(dataset)
768
829
  return dataset
769
830
 
@@ -787,9 +848,10 @@ class GHZConfiguration(BenchmarkConfigurationBase):
787
848
  custom_qubits_array (Optional[Sequence[Sequence[int]]]): A sequence (e.g., Tuple or List) of sequences of
788
849
  physical qubit layouts, as specified by integer labels, where the benchmark is meant to be run.
789
850
  * If None, takes all qubits specified in the backend coupling map.
790
- qubit_counts (Optional[Sequence[int]]): A sequence (e.g., Tuple or List) of integers denoting number of qubits
851
+ qubit_counts (Optional[Sequence[int]]): CURRENTLY NOT SUPPORTED, A sequence (e.g., Tuple or List) of integers
852
+ denoting number of qubits
791
853
  for which the benchmark is meant to be run. The largest qubit count provided here has to be smaller than the
792
- smallest given qubit layout.
854
+ smalles given qubit layout.
793
855
  qiskit_optim_level (int): The optimization level used for transpilation to backend architecture.
794
856
  * Default: 3
795
857
  optimize_sqg (bool): Whether consecutive single qubit gates are optimized for reduced gate count via
@@ -814,13 +876,13 @@ class GHZConfiguration(BenchmarkConfigurationBase):
814
876
 
815
877
  benchmark: Type[Benchmark] = GHZBenchmark
816
878
  state_generation_routine: str = "tree"
817
- choose_qubits_routine: str = "custom"
818
- custom_qubits_array: Optional[List[List[int]]] = None
819
- qubit_counts: Optional[List[int]] = None
879
+ custom_qubits_array: Optional[Sequence[Sequence[int]]] = None
880
+ qubit_counts: Optional[Sequence[int]] = None
881
+ shots: int = 2**10
820
882
  qiskit_optim_level: int = 3
821
883
  optimize_sqg: bool = True
822
884
  fidelity_routine: str = "coherences"
823
- num_RMs: Optional[int] = 24
885
+ num_RMs: Optional[int] = 100
824
886
  rem: bool = True
825
887
  mit_shots: int = 1_000
826
888
  cal_url: Optional[str] = None
@@ -650,7 +650,6 @@ def plot_rb_decay(
650
650
  decay_rate[rb_type] = {
651
651
  str(q): dataset.attrs[q_idx][rb_type]["decay_rate"]["value"] for q_idx, q in enumerate(qubits_array)
652
652
  }
653
- print(dataset.attrs)
654
653
  offset[rb_type] = {
655
654
  str(q): dataset.attrs[q_idx][rb_type]["fit_offset"]["value"] for q_idx, q in enumerate(qubits_array)
656
655
  }
@@ -770,11 +769,14 @@ def plot_rb_decay(
770
769
  fidelity_stderr[key][str(qubits)] = np.nan
771
770
 
772
771
  if identifier == "mrb":
773
- plot_label = fr"$\overline{{F}}_\text{{MRB}} (n={len(qubits)})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
772
+ plot_label = fr"$\overline{{F}}_{{MRB}} (n={len(qubits)})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
774
773
  elif key == "interleaved":
775
- plot_label = fr"$\overline{{F}}_\text{{{interleaved_gate}}} ({qubits})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
774
+ plot_label = fr"$\overline{{F}}_{{interleaved_gate}} ({qubits})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
776
775
  else:
777
- plot_label = fr"$\overline{{F}}_\text{{Clifford}} ({qubits})$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
776
+ print(fidelity_value)
777
+ print(qubits)
778
+ print(key)
779
+ plot_label = fr"$\overline{{F}}_{{CRB}}$ = {100.0 * fidelity_value[key][str(qubits)]:.2f} +/- {100.0 * fidelity_stderr[key][str(qubits)]:.2f} (%)"
778
780
 
779
781
  ax.plot(
780
782
  x_linspace,
@@ -25,7 +25,7 @@ from mthree.classes import QuasiCollection
25
25
  from mthree.exceptions import M3Error
26
26
  from mthree.mitigation import _job_thread
27
27
  from mthree.utils import final_measurement_mapping
28
- from qiskit import QuantumCircuit, execute # pylint: disable = no-name-in-module
28
+ from qiskit import QuantumCircuit, transpile # pylint: disable = no-name-in-module
29
29
  from qiskit.providers import Backend, BackendV1, BackendV2
30
30
 
31
31
  from iqm.benchmarks.utils import get_iqm_backend, timeit
@@ -201,13 +201,8 @@ class M3IQM(mthree.M3Mitigation):
201
201
  jobs = []
202
202
  if not isinstance(self.system, Backend):
203
203
  for circs in circs_list:
204
- _job = execute(
205
- circs,
206
- self.system,
207
- optimization_level=0,
208
- shots=shots,
209
- rep_delay=self.rep_delay,
210
- )
204
+ transpiled_circuit = transpile(circs, self.system, optimization_level=0)
205
+ _job = self.system.run(transpiled_circuit, shots=shots, rep_delay=self.rep_delay)
211
206
  jobs.append(_job)
212
207
 
213
208
  # *****************************************