iqm-benchmarks 2.44__py3-none-any.whl → 2.46__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.
- iqm/benchmarks/benchmark_definition.py +1 -0
- iqm/benchmarks/compressive_gst/compressive_gst.py +100 -46
- iqm/benchmarks/compressive_gst/gst_analysis.py +480 -381
- iqm/benchmarks/entanglement/ghz.py +2 -2
- iqm/benchmarks/optimization/qscore.py +7 -6
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/METADATA +2 -2
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/RECORD +17 -17
- mGST/additional_fns.py +35 -20
- mGST/algorithm.py +73 -57
- mGST/low_level_jit.py +122 -56
- mGST/optimization.py +12 -6
- mGST/qiskit_interface.py +64 -87
- mGST/reporting/figure_gen.py +390 -57
- mGST/reporting/reporting.py +209 -11
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.44.dist-info → iqm_benchmarks-2.46.dist-info}/top_level.txt +0 -0
|
@@ -171,6 +171,7 @@ def add_counts_to_dataset(counts: List[Dict[str, int]], identifier: str, dataset
|
|
|
171
171
|
dataset_merged: xarray.Dataset
|
|
172
172
|
A merged dataset where the new counts are added the input dataset
|
|
173
173
|
"""
|
|
174
|
+
qcvv_logger.info(f"Adding counts to dataset")
|
|
174
175
|
if not isinstance(counts, list):
|
|
175
176
|
counts = [counts]
|
|
176
177
|
datasets = []
|
|
@@ -39,9 +39,9 @@ from iqm.benchmarks.circuit_containers import BenchmarkCircuit, CircuitGroup, Ci
|
|
|
39
39
|
from iqm.benchmarks.compressive_gst.gst_analysis import mgst_analysis
|
|
40
40
|
from iqm.benchmarks.logging_config import qcvv_logger
|
|
41
41
|
from iqm.benchmarks.utils import (
|
|
42
|
+
get_active_qubits,
|
|
42
43
|
perform_backend_transpilation,
|
|
43
44
|
retrieve_all_counts,
|
|
44
|
-
set_coupling_map,
|
|
45
45
|
submit_execute,
|
|
46
46
|
timeit,
|
|
47
47
|
)
|
|
@@ -73,10 +73,13 @@ class CompressiveGST(Benchmark):
|
|
|
73
73
|
self.num_qubits = len(self.qubit_layouts[0])
|
|
74
74
|
self.pdim = 2**self.num_qubits
|
|
75
75
|
self.num_povm = self.pdim
|
|
76
|
+
self.verbose_level = configuration.verbose_level
|
|
76
77
|
|
|
77
78
|
self.gate_set, self.gate_labels, self.num_gates = parse_gate_set(
|
|
78
79
|
configuration, self.num_qubits, self.qubit_layouts
|
|
79
80
|
)
|
|
81
|
+
self.gate_context = configuration.gate_context
|
|
82
|
+
validate_gate_context(self)
|
|
80
83
|
|
|
81
84
|
if configuration.opt_method not in ["GD", "SFN", "auto"]:
|
|
82
85
|
raise ValueError("Invalid optimization method, valid options are: GD, SFN, auto")
|
|
@@ -92,6 +95,8 @@ class CompressiveGST(Benchmark):
|
|
|
92
95
|
self.max_iterations = [250, 250]
|
|
93
96
|
elif isinstance(configuration.max_iterations, list):
|
|
94
97
|
self.max_iterations = configuration.max_iterations
|
|
98
|
+
elif isinstance(configuration.max_iterations, int):
|
|
99
|
+
self.max_iterations = [configuration.max_iterations, configuration.max_iterations]
|
|
95
100
|
if configuration.batch_size == "auto":
|
|
96
101
|
self.batch_size = 30 * self.pdim
|
|
97
102
|
else:
|
|
@@ -140,6 +145,7 @@ class CompressiveGST(Benchmark):
|
|
|
140
145
|
raw_qc_list = qiskit_interface.get_qiskit_circuits(
|
|
141
146
|
gate_circuits, self.gate_set, self.num_qubits, unmapped_qubits
|
|
142
147
|
)
|
|
148
|
+
|
|
143
149
|
if "move" in self.backend.operation_names:
|
|
144
150
|
qcvv_logger.warning(
|
|
145
151
|
f"Transpilation on star-architectures currently allows move gates to transit barriers, "
|
|
@@ -150,33 +156,36 @@ class CompressiveGST(Benchmark):
|
|
|
150
156
|
qcvv_logger.info(
|
|
151
157
|
f"Will transpile all {self.configuration.num_circuits} circuits according to fixed physical layout"
|
|
152
158
|
)
|
|
159
|
+
|
|
160
|
+
if "move" in self.backend.operation_names:
|
|
161
|
+
backend_qubits = np.arange(1, self.backend.num_qubits)
|
|
162
|
+
qubit_layouts = [[q - 1 for q in layout] for layout in self.qubit_layouts]
|
|
163
|
+
else:
|
|
164
|
+
backend_qubits = np.arange(self.backend.num_qubits)
|
|
165
|
+
qubit_layouts = self.qubit_layouts
|
|
166
|
+
|
|
153
167
|
if self.configuration.parallel_execution:
|
|
154
|
-
all_qubits = [qubit for layout in
|
|
168
|
+
all_qubits = [qubit for layout in qubit_layouts for qubit in layout]
|
|
155
169
|
if len(all_qubits) != len(set(all_qubits)):
|
|
156
170
|
raise ValueError(
|
|
157
171
|
"Qubit layouts can't overlap when parallel_execution is enabled, please choose non-overlapping layouts."
|
|
158
172
|
)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
clbits = np.arange(self.num_qubits)
|
|
169
|
-
for qubit_layout in qubit_layouts:
|
|
170
|
-
circ_parallel.compose(circ, qubits=qubit_layout, clbits=clbits, inplace=True)
|
|
171
|
-
clbits += self.num_qubits
|
|
172
|
-
raw_qc_list_parallel.append(circ_parallel)
|
|
173
|
+
# For each gate sequence, create a circuit with gate context and GST sequence on all qubits in the layout
|
|
174
|
+
composed_qc_list = qiskit_interface.get_composed_qiskit_circuits(
|
|
175
|
+
gate_circuits,
|
|
176
|
+
self.gate_set,
|
|
177
|
+
self.backend.num_qubits,
|
|
178
|
+
qubit_layouts,
|
|
179
|
+
gate_context=self.gate_context,
|
|
180
|
+
parallel=True,
|
|
181
|
+
)
|
|
173
182
|
transpiled_qc_list, _ = perform_backend_transpilation(
|
|
174
|
-
|
|
183
|
+
composed_qc_list,
|
|
175
184
|
self.backend,
|
|
176
185
|
qubits=backend_qubits,
|
|
177
186
|
coupling_map=self.backend.coupling_map,
|
|
178
187
|
qiskit_optim_level=0,
|
|
179
|
-
optimize_sqg=
|
|
188
|
+
optimize_sqg=True,
|
|
180
189
|
drop_final_rz=False,
|
|
181
190
|
)
|
|
182
191
|
for qubits in self.qubit_layouts:
|
|
@@ -184,13 +193,21 @@ class CompressiveGST(Benchmark):
|
|
|
184
193
|
transpiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=transpiled_qc_list))
|
|
185
194
|
untranspiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=raw_qc_list))
|
|
186
195
|
else:
|
|
187
|
-
for
|
|
188
|
-
|
|
196
|
+
# for parralel = False, a unique list of circuits is generated for each qubit layout
|
|
197
|
+
composed_qc_list = qiskit_interface.get_composed_qiskit_circuits(
|
|
198
|
+
gate_circuits,
|
|
199
|
+
self.gate_set,
|
|
200
|
+
self.backend.num_qubits,
|
|
201
|
+
qubit_layouts,
|
|
202
|
+
gate_context=self.gate_context,
|
|
203
|
+
parallel=False,
|
|
204
|
+
)
|
|
205
|
+
for idx, qubits in enumerate(self.qubit_layouts):
|
|
189
206
|
transpiled_qc_list, _ = perform_backend_transpilation(
|
|
190
|
-
|
|
207
|
+
composed_qc_list[idx],
|
|
191
208
|
self.backend,
|
|
192
|
-
|
|
193
|
-
coupling_map=coupling_map,
|
|
209
|
+
backend_qubits,
|
|
210
|
+
coupling_map=self.backend.coupling_map,
|
|
194
211
|
qiskit_optim_level=0,
|
|
195
212
|
optimize_sqg=False,
|
|
196
213
|
drop_final_rz=False,
|
|
@@ -198,6 +215,7 @@ class CompressiveGST(Benchmark):
|
|
|
198
215
|
# Saving raw and transpiled circuits in a consistent format with other benchmarks
|
|
199
216
|
transpiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=transpiled_qc_list))
|
|
200
217
|
untranspiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=raw_qc_list))
|
|
218
|
+
|
|
201
219
|
return transpiled_circuits, untranspiled_circuits
|
|
202
220
|
|
|
203
221
|
def add_configuration_to_dataset(self, dataset): # CHECK
|
|
@@ -210,13 +228,14 @@ class CompressiveGST(Benchmark):
|
|
|
210
228
|
dataset: xarray.Dataset to be used for further data storage
|
|
211
229
|
"""
|
|
212
230
|
# Adding configuration entries and class variables, prioritizing the latter in case of conflicts
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
elif key == "backend":
|
|
231
|
+
avoided_keys = ["runs", "configuration", "circuits"]
|
|
232
|
+
for key, value in (self.serializable_configuration.__dict__ | self.__dict__).items():
|
|
233
|
+
if key == "backend":
|
|
217
234
|
dataset.attrs[key] = value.name
|
|
218
|
-
|
|
235
|
+
elif key not in avoided_keys:
|
|
219
236
|
dataset.attrs[key] = value
|
|
237
|
+
else:
|
|
238
|
+
pass
|
|
220
239
|
dataset.attrs["gauge_weights"] = dict({f"G%i" % i: 1 for i in range(self.num_gates)}, **{"spam": 0.1})
|
|
221
240
|
|
|
222
241
|
def execute(self, backend) -> xr.Dataset:
|
|
@@ -225,7 +244,7 @@ class CompressiveGST(Benchmark):
|
|
|
225
244
|
"""
|
|
226
245
|
|
|
227
246
|
dataset = xr.Dataset()
|
|
228
|
-
qcvv_logger.info(f"
|
|
247
|
+
qcvv_logger.info(f"Generating {self.configuration.num_circuits} random GST circuits")
|
|
229
248
|
|
|
230
249
|
self.circuits = Circuits()
|
|
231
250
|
# Generate circuits
|
|
@@ -269,6 +288,7 @@ class CompressiveGST(Benchmark):
|
|
|
269
288
|
|
|
270
289
|
self.circuits.benchmark_circuits = [transpiled_circuits, untranspiled_circuits]
|
|
271
290
|
self.add_configuration_to_dataset(dataset)
|
|
291
|
+
qcvv_logger.info(f"Run completed")
|
|
272
292
|
return dataset
|
|
273
293
|
|
|
274
294
|
|
|
@@ -325,12 +345,15 @@ class GSTConfiguration(BenchmarkConfigurationBase):
|
|
|
325
345
|
* Default: "auto"
|
|
326
346
|
bootstrap_samples (int): The number of times the optimization algorithm is repeated on fake data to estimate
|
|
327
347
|
the uncertainty via bootstrapping.
|
|
348
|
+
verbose_level (int): The level of verbosity of the output. 0 is minimal, 1 gives optimization updates, 2 outputs optimization plots
|
|
349
|
+
* Default: 1
|
|
328
350
|
parallel_execution (bool): Whether to run the circuits for all layouts in parallel on the backend.
|
|
329
351
|
"""
|
|
330
352
|
|
|
331
353
|
benchmark: Type[Benchmark] = CompressiveGST
|
|
332
354
|
qubit_layouts: Union[List[int], List[List[int]]]
|
|
333
355
|
gate_set: Union[str, List[Any]]
|
|
356
|
+
gate_context: Union[Any, List[Any], None] = None
|
|
334
357
|
num_circuits: int
|
|
335
358
|
rank: int
|
|
336
359
|
shots: int = 2**10
|
|
@@ -339,11 +362,11 @@ class GSTConfiguration(BenchmarkConfigurationBase):
|
|
|
339
362
|
from_init: bool = True
|
|
340
363
|
max_inits: int = 20
|
|
341
364
|
opt_method: str = "auto"
|
|
342
|
-
max_iterations: Union[str, List[int]] = "auto"
|
|
343
|
-
convergence_criteria: Union[str, List[float]] = [4, 1e-
|
|
365
|
+
max_iterations: Union[str, List[int], int] = "auto"
|
|
366
|
+
convergence_criteria: Union[str, List[float]] = [4, 1e-5]
|
|
344
367
|
batch_size: Union[str, int] = "auto"
|
|
345
368
|
bootstrap_samples: int = 0
|
|
346
|
-
|
|
369
|
+
verbose_level: int = 1
|
|
347
370
|
parallel_execution: bool = False
|
|
348
371
|
|
|
349
372
|
|
|
@@ -373,6 +396,31 @@ def parse_layouts(qubit_layouts: Union[List[int], List[List[int]]]) -> List[List
|
|
|
373
396
|
)
|
|
374
397
|
|
|
375
398
|
|
|
399
|
+
def validate_gate_context(self):
|
|
400
|
+
"""Validate that the gate context is properly configured.
|
|
401
|
+
|
|
402
|
+
Checks that:
|
|
403
|
+
1. If gate_context is a list, it has the same length as gate_set
|
|
404
|
+
2. The qubits used in gate_context don't overlap with qubits in the layouts
|
|
405
|
+
|
|
406
|
+
Raises:
|
|
407
|
+
ValueError: If gate_context is invalid or if qubits in gate_context overlap with qubits in layouts
|
|
408
|
+
"""
|
|
409
|
+
if self.gate_context is not None:
|
|
410
|
+
if isinstance(self.gate_context, list):
|
|
411
|
+
if len(self.gate_context) != len(self.gate_set):
|
|
412
|
+
raise ValueError("If gate_context is a list, it must have the same length as gate_set.")
|
|
413
|
+
# Check that context circuits don't overlap with qubit layouts for GST circuits
|
|
414
|
+
context_qubits = [q for qc in self.gate_context for q in get_active_qubits(qc)]
|
|
415
|
+
else:
|
|
416
|
+
context_qubits = get_active_qubits(self.gate_context)
|
|
417
|
+
layout_qubits = [q for layout in self.qubit_layouts for q in layout]
|
|
418
|
+
if any(q in layout_qubits for q in context_qubits):
|
|
419
|
+
raise ValueError(
|
|
420
|
+
f"Gate context qubits {set(context_qubits)} must not overlap with qubits in layouts {set(layout_qubits)}."
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
376
424
|
def parse_gate_set(
|
|
377
425
|
configuration: GSTConfiguration, num_qubits: int, qubit_layouts: List[List[int]]
|
|
378
426
|
) -> Tuple[List[QuantumCircuit], Dict[str, Dict[int, str]], int]:
|
|
@@ -398,15 +446,15 @@ def parse_gate_set(
|
|
|
398
446
|
"""
|
|
399
447
|
if isinstance(configuration.gate_set, str) and configuration.gate_set not in [
|
|
400
448
|
"1QXYI",
|
|
401
|
-
"
|
|
449
|
+
"2QXYICZ",
|
|
402
450
|
"2QXYCZ_extended",
|
|
403
451
|
"3QXYCZ",
|
|
404
452
|
]:
|
|
405
453
|
raise ValueError(
|
|
406
454
|
"No gate set of the specified name is implemented, please choose among "
|
|
407
|
-
"1QXYI,
|
|
455
|
+
"1QXYI, 2QXYICZ, 2QXYCZ_extended, 3QXYCZ."
|
|
408
456
|
)
|
|
409
|
-
if configuration.gate_set in ["1QXYI", "
|
|
457
|
+
if configuration.gate_set in ["1QXYI", "2QXYICZ", "2QXYCZ_extended", "3QXYCZ"]:
|
|
410
458
|
gate_set, gate_label_dict, num_gates = create_predefined_gate_set(
|
|
411
459
|
configuration.gate_set, num_qubits, qubit_layouts
|
|
412
460
|
)
|
|
@@ -430,11 +478,12 @@ def parse_gate_set(
|
|
|
430
478
|
return gate_set, gate_label_dict, num_gates
|
|
431
479
|
|
|
432
480
|
raise ValueError(
|
|
433
|
-
f"Invalid gate set, choose among 1QXYI,
|
|
481
|
+
f"Invalid gate set, choose among 1QXYI, 2QXYICZ, 2QXYCZ_extended,"
|
|
434
482
|
f" 3QXYCZ or provide a list of Qiskti circuits to define the gates."
|
|
435
483
|
)
|
|
436
484
|
|
|
437
485
|
|
|
486
|
+
# pylint: disable=too-many-statements
|
|
438
487
|
def create_predefined_gate_set(
|
|
439
488
|
gate_set: Union[str, List[Any]], num_qubits: int, qubit_layouts: List[List[int]]
|
|
440
489
|
) -> Tuple[List[QuantumCircuit], Dict[str, Dict[int, str]], int]:
|
|
@@ -456,23 +505,28 @@ def create_predefined_gate_set(
|
|
|
456
505
|
|
|
457
506
|
"""
|
|
458
507
|
unmapped_qubits = list(np.arange(num_qubits))
|
|
508
|
+
# Define an idle gate using a time delay comparable to single qubit gate duration
|
|
509
|
+
Idle = QuantumCircuit(num_qubits, 0)
|
|
510
|
+
Idle.delay(32e-9, unit="s")
|
|
459
511
|
|
|
512
|
+
# Define the gate set
|
|
460
513
|
if gate_set == "1QXYI":
|
|
461
|
-
gate_list = [
|
|
514
|
+
gate_list = [Idle, RGate(0.5 * np.pi, 0), RGate(0.5 * np.pi, np.pi / 2)]
|
|
462
515
|
gates = [QuantumCircuit(num_qubits, 0) for _ in range(len(gate_list))]
|
|
463
516
|
gate_qubits = [[0], [0], [0]]
|
|
464
517
|
for i, gate in enumerate(gate_list):
|
|
465
518
|
gates[i].append(gate, gate_qubits[i])
|
|
466
519
|
gate_labels = ["Idle", "Rx_pi_2", "Ry_pi_2"]
|
|
467
|
-
elif gate_set == "
|
|
468
|
-
gate_qubits = [[0], [1], [0], [1], [0, 1]]
|
|
469
|
-
gates = [QuantumCircuit(num_qubits, 0) for _ in range(
|
|
470
|
-
gates[0].append(
|
|
471
|
-
gates[1].append(RGate(0.5 * np.pi, 0), [
|
|
472
|
-
gates[2].append(RGate(0.5 * np.pi,
|
|
473
|
-
gates[3].append(RGate(0.5 * np.pi, np.pi / 2), [
|
|
474
|
-
gates[4].append(
|
|
475
|
-
|
|
520
|
+
elif gate_set == "2QXYICZ":
|
|
521
|
+
gate_qubits = [[0], [0], [1], [0], [1], [0, 1]]
|
|
522
|
+
gates = [QuantumCircuit(num_qubits, 0) for _ in range(6)]
|
|
523
|
+
gates[0].append(Idle, [0, 1])
|
|
524
|
+
gates[1].append(RGate(0.5 * np.pi, 0), [0])
|
|
525
|
+
gates[2].append(RGate(0.5 * np.pi, 0), [1])
|
|
526
|
+
gates[3].append(RGate(0.5 * np.pi, np.pi / 2), [0])
|
|
527
|
+
gates[4].append(RGate(0.5 * np.pi, np.pi / 2), [1])
|
|
528
|
+
gates[5].append(CZGate(), [0, 1])
|
|
529
|
+
gate_labels = ["Idle", "Rx_pi_2", "Rx_pi_2", "Ry_pi_2", "Ry_pi_2", "cz"]
|
|
476
530
|
elif gate_set == "2QXYCZ_extended":
|
|
477
531
|
gate_qubits = [[0], [1], [0], [1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
|
|
478
532
|
gates = [QuantumCircuit(num_qubits, 0) for _ in range(9)]
|