iqm-benchmarks 1.3__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.

Files changed (42) hide show
  1. iqm/benchmarks/__init__.py +31 -0
  2. iqm/benchmarks/benchmark.py +109 -0
  3. iqm/benchmarks/benchmark_definition.py +264 -0
  4. iqm/benchmarks/benchmark_experiment.py +163 -0
  5. iqm/benchmarks/compressive_gst/__init__.py +20 -0
  6. iqm/benchmarks/compressive_gst/compressive_gst.py +1029 -0
  7. iqm/benchmarks/entanglement/__init__.py +18 -0
  8. iqm/benchmarks/entanglement/ghz.py +802 -0
  9. iqm/benchmarks/logging_config.py +29 -0
  10. iqm/benchmarks/optimization/__init__.py +18 -0
  11. iqm/benchmarks/optimization/qscore.py +719 -0
  12. iqm/benchmarks/quantum_volume/__init__.py +21 -0
  13. iqm/benchmarks/quantum_volume/clops.py +726 -0
  14. iqm/benchmarks/quantum_volume/quantum_volume.py +854 -0
  15. iqm/benchmarks/randomized_benchmarking/__init__.py +18 -0
  16. iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl +0 -0
  17. iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl +0 -0
  18. iqm/benchmarks/randomized_benchmarking/clifford_rb/__init__.py +19 -0
  19. iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py +386 -0
  20. iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py +19 -0
  21. iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py +555 -0
  22. iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py +19 -0
  23. iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py +810 -0
  24. iqm/benchmarks/randomized_benchmarking/multi_lmfit.py +86 -0
  25. iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py +892 -0
  26. iqm/benchmarks/readout_mitigation.py +290 -0
  27. iqm/benchmarks/utils.py +521 -0
  28. iqm_benchmarks-1.3.dist-info/LICENSE +205 -0
  29. iqm_benchmarks-1.3.dist-info/METADATA +190 -0
  30. iqm_benchmarks-1.3.dist-info/RECORD +42 -0
  31. iqm_benchmarks-1.3.dist-info/WHEEL +5 -0
  32. iqm_benchmarks-1.3.dist-info/top_level.txt +2 -0
  33. mGST/LICENSE +21 -0
  34. mGST/README.md +54 -0
  35. mGST/additional_fns.py +962 -0
  36. mGST/algorithm.py +733 -0
  37. mGST/compatibility.py +238 -0
  38. mGST/low_level_jit.py +694 -0
  39. mGST/optimization.py +349 -0
  40. mGST/qiskit_interface.py +282 -0
  41. mGST/reporting/figure_gen.py +334 -0
  42. mGST/reporting/reporting.py +710 -0
@@ -0,0 +1,290 @@
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
+ """M3 modification for readout mitigation at IQM QPU's."""
15
+ import logging
16
+ from math import ceil
17
+ import sys
18
+ import threading
19
+ from typing import Any, Dict, Iterable, List
20
+ import warnings
21
+
22
+ import mthree
23
+ from mthree.circuits import _marg_meas_states, _tensor_meas_states, balanced_cal_circuits, balanced_cal_strings
24
+ from mthree.classes import QuasiCollection
25
+ from mthree.exceptions import M3Error
26
+ from mthree.mitigation import _job_thread
27
+ from mthree.utils import final_measurement_mapping
28
+ from qiskit import QuantumCircuit, execute # pylint: disable = no-name-in-module
29
+ from qiskit.providers import Backend, BackendV1, BackendV2
30
+
31
+ from iqm.benchmarks.utils import get_iqm_backend, timeit
32
+ from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
33
+
34
+
35
+ # The code here is close to the original M3 code licenced under Apache 2 as well (https://github.com/Qiskit/qiskit-addon-mthree/blob/main/LICENSE.txt).
36
+ # We deactivate this to not break functionality.
37
+ # pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-locals, too-many-branches, too-many-statements,
38
+ # pylint: disable=attribute-defined-outside-init, too-few-public-methods, access-member-before-definition
39
+ class M3IQM(mthree.M3Mitigation):
40
+ """M3 readout mitigation class modified to work with IQM devices."""
41
+
42
+ def cals_from_system(
43
+ self,
44
+ qubits=None,
45
+ shots=None,
46
+ method=None,
47
+ initial_reset=False,
48
+ rep_delay=None,
49
+ cals_file=None,
50
+ async_cal=False,
51
+ cal_id=None,
52
+ ):
53
+ """Grab calibration data from system.
54
+
55
+ Parameters:
56
+ qubits (array_like): Qubits over which to correct calibration data. Default is all.
57
+ shots (int): Number of shots per circuit. min(1e4, max_shots).
58
+ method (str): Type of calibration, 'balanced' (default for hardware),
59
+ 'independent' (default for simulators), or 'marginal'.
60
+ initial_reset (bool): Use resets at beginning of calibration circuits, default=False.
61
+ rep_delay (float): Delay between circuits on IBM Quantum backends.
62
+ cals_file (str): Output path to write JSON calibration data to.
63
+ async_cal (bool): Do calibration async in a separate thread, default is False.
64
+
65
+ Raises:
66
+ M3Error: Called while a calibration currently in progress.
67
+ """
68
+ if self._thread:
69
+ raise M3Error("Calibration currently in progress.")
70
+ if qubits is None:
71
+ qubits = range(self.num_qubits)
72
+ # Remove faulty qubits if any
73
+ if any(self.system_info["inoperable_qubits"]):
74
+ qubits = list(
75
+ filter(
76
+ lambda item: item not in self.system_info["inoperable_qubits"],
77
+ list(range(self.num_qubits)),
78
+ )
79
+ )
80
+ warnings.warn(
81
+ f"Backend reporting inoperable qubits. Skipping calibrations for: %s",
82
+ self.system_info["inoperable_qubits"],
83
+ )
84
+
85
+ if method is None:
86
+ method = "balanced"
87
+ # if self.system_info["simulator"]:
88
+ # method = "independent"
89
+ self.cal_method = method
90
+ self.rep_delay = rep_delay
91
+ self.cals_file = cals_file
92
+ self.cal_timestamp = None
93
+ self._grab_additional_cals(
94
+ qubits,
95
+ shots=shots,
96
+ method=method,
97
+ rep_delay=rep_delay,
98
+ initial_reset=initial_reset,
99
+ async_cal=async_cal,
100
+ cal_id=cal_id,
101
+ )
102
+
103
+ def _grab_additional_cals(
104
+ self,
105
+ qubits,
106
+ shots=None,
107
+ method="balanced",
108
+ rep_delay=None,
109
+ initial_reset=False,
110
+ async_cal=False,
111
+ cal_id=None,
112
+ ):
113
+ """Grab missing calibration data from backend.
114
+
115
+ Parameters:
116
+ qubits (array_like): List of measured qubits.
117
+ shots (int): Number of shots to take, min(1e4, max_shots).
118
+ method (str): Type of calibration, 'balanced' (default), 'independent', or 'marginal'.
119
+ rep_delay (float): Delay between circuits on IBM Quantum backends.
120
+ initial_reset (bool): Use resets at beginning of calibration circuits, default=False.
121
+ async_cal (bool): Do calibration async in a separate thread, default is False.
122
+
123
+ Raises:
124
+ M3Error: Backend not set.
125
+ M3Error: Faulty qubits found.
126
+ """
127
+ if self.system is None:
128
+ raise M3Error("System is not set. Use 'cals_from_file'.")
129
+ if self.single_qubit_cals is None:
130
+ self.single_qubit_cals = [None] * self.num_qubits
131
+ if self.cal_shots is None:
132
+ if shots is None:
133
+ shots = min(self.system_info["max_shots"], 10000)
134
+ self.cal_shots = shots
135
+ if self.rep_delay is None:
136
+ self.rep_delay = rep_delay
137
+
138
+ if method not in ["independent", "balanced", "marginal"]:
139
+ raise M3Error(
140
+ f"Invalid calibration method: {method}. Valid methods are 'independent', 'balanced', or 'marginal'."
141
+ )
142
+
143
+ if isinstance(qubits, dict):
144
+ # Assuming passed a mapping
145
+ qubits = list(set(qubits.values()))
146
+ elif isinstance(qubits, list):
147
+ # Check if passed a list of mappings
148
+ if isinstance(qubits[0], dict):
149
+ # Assuming list of mappings, need to get unique elements
150
+ _qubits = []
151
+ for item in qubits:
152
+ _qubits.extend(list(set(item.values())))
153
+ qubits = list(set(_qubits))
154
+
155
+ # Do check for inoperable qubits here
156
+ inoperable_overlap = list(set(qubits) & set(self.system_info["inoperable_qubits"]))
157
+ if any(inoperable_overlap):
158
+ raise M3Error(f"Attempting to calibrate inoperable qubits: {inoperable_overlap}")
159
+
160
+ num_cal_qubits = len(qubits)
161
+ cal_strings = []
162
+ # shots is needed here because balanced cals will use a value
163
+ # different from cal_shots
164
+ shots = self.cal_shots
165
+ if method == "marginal":
166
+ trans_qcs = _marg_meas_states(qubits, self.num_qubits, initial_reset=initial_reset)
167
+ elif method == "balanced":
168
+ cal_strings = balanced_cal_strings(num_cal_qubits)
169
+ trans_qcs = balanced_cal_circuits(cal_strings, qubits, self.num_qubits, initial_reset=initial_reset)
170
+ shots = self.cal_shots // num_cal_qubits
171
+ if self.cal_shots / num_cal_qubits != shots:
172
+ shots += 1
173
+ self._balanced_shots = shots * num_cal_qubits
174
+ # Independent
175
+ else:
176
+ trans_qcs = []
177
+ for qubit in qubits:
178
+ trans_qcs.extend(_tensor_meas_states(qubit, self.num_qubits, initial_reset=initial_reset))
179
+
180
+ num_circs = len(trans_qcs)
181
+ # check for max number of circuits per job
182
+ if isinstance(self.system, BackendV1):
183
+ # Aer simulator has no 'max_experiments'
184
+ max_circuits = getattr(self.system.configuration(), "max_experiments", 300)
185
+ elif isinstance(self.system, BackendV2):
186
+ max_circuits = self.system.max_circuits
187
+ # Needed for https://github.com/Qiskit/qiskit-terra/issues/9947
188
+ if max_circuits is None:
189
+ max_circuits = 300
190
+ else:
191
+ raise M3Error("Unknown backend type")
192
+ # Determine the number of jobs required
193
+ num_jobs = ceil(num_circs / max_circuits)
194
+ # Get the slice length
195
+ circ_slice = ceil(num_circs / num_jobs)
196
+ circs_list = [trans_qcs[kk * circ_slice : (kk + 1) * circ_slice] for kk in range(num_jobs - 1)] + [
197
+ trans_qcs[(num_jobs - 1) * circ_slice :]
198
+ ]
199
+ # Do job submission here
200
+ # This Backend check is here for Qiskit direct access. Should be removed later.
201
+ jobs = []
202
+ if not isinstance(self.system, Backend):
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
+ )
211
+ jobs.append(_job)
212
+
213
+ # *****************************************
214
+ else: # That's what IQM backend do!
215
+ # *****************************************
216
+ for circs in circs_list:
217
+ if cal_id is None:
218
+ _job = self.system.run(circs, shots=shots, rep_delay=self.rep_delay)
219
+ else:
220
+ _job = self.system.run(
221
+ circs,
222
+ shots=shots,
223
+ rep_delay=self.rep_delay,
224
+ calibration_set_id=cal_id,
225
+ )
226
+ jobs.append(_job)
227
+ print(f"{len(circs)} calibration circuits ready to be executed!")
228
+
229
+ # Execute job and cal building in new thread.
230
+ self._job_error = None
231
+ if async_cal:
232
+ thread = threading.Thread(
233
+ target=_job_thread,
234
+ args=(jobs, self, qubits, num_cal_qubits, cal_strings),
235
+ )
236
+ self._thread = thread
237
+ self._thread.start()
238
+ else:
239
+ _job_thread(jobs, self, qubits, num_cal_qubits, cal_strings)
240
+
241
+
242
+ def readout_error_m3(counts: Dict[str, float], mit: M3IQM, qubits: Iterable) -> Dict[str, float]:
243
+ """Counts processor using M3IQM for readout error mitigation.
244
+
245
+ The qubits argument can be either a dictionary coming from `mthree.utils.final_measurement_mapping`
246
+ or an array like the initial layout coming from [backend.qubit_name_to_index(name)]
247
+
248
+ returns a dictionary of quasiprobabilties.
249
+
250
+ NOTE: we could also pass a list of input counts and then this would return a list of quasiprobabilities.
251
+ This would not work out of the box for us since we need the annotations of either Dict or List (not Union).
252
+ """
253
+ return mit.apply_correction(counts, qubits)
254
+
255
+
256
+ @timeit
257
+ def apply_readout_error_mitigation(
258
+ backend_arg: str | IQMBackendBase,
259
+ transpiled_circuits: List[QuantumCircuit],
260
+ counts: List[Dict[str, int]],
261
+ mit_shots: int = 1000,
262
+ ) -> List[tuple[Any, Any]] | List[tuple[QuasiCollection, list]] | List[QuasiCollection]:
263
+ """
264
+ Args:
265
+ backend_arg (str | IQMBackendBase): the backend to calibrate an M3 mitigator against.
266
+ transpiled_circuits (List[QuantumCircuit]): the list of transpiled quantum circuits.
267
+ counts (List[Dict[str, int]]): the measurement counts corresponding to the circuits.
268
+ mit_shots (int): number of shots per circuit.
269
+ Returns:
270
+ tuple[Any, Any] | tuple[QuasiCollection, list] | QuasiCollection: a list of dictionaries with REM-corrected quasiprobabilities for each outcome.
271
+ """
272
+ # M3IQM uses mthree.mitigation, which for some reason displays many INFO messages
273
+ # Not sure if this is the best way to suppress them; MODIFY IF NECESSARY !
274
+ warnings.warn("Suppressing INFO messages from M3IQM with logging.disable(sys.maxsize) - update if problematic!")
275
+ logging.disable(sys.maxsize)
276
+
277
+ if isinstance(backend_arg, str):
278
+ backend = get_iqm_backend(backend_arg)
279
+ else:
280
+ backend = backend_arg
281
+
282
+ # Initialize with the given system and get calibration data
283
+ qubits_rem = [final_measurement_mapping(c) for c in transpiled_circuits]
284
+
285
+ mit = M3IQM(backend)
286
+ mit.cals_from_system(qubits_rem, shots=mit_shots)
287
+ # Apply the REM correction to the given measured counts
288
+ rem_quasidistro = [mit.apply_correction(c, q) for c, q in zip(counts, qubits_rem)]
289
+
290
+ return rem_quasidistro