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.
@@ -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 self.qubit_layouts for qubit in layout]
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
- raw_qc_list_parallel = []
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
- for circ in raw_qc_list:
167
- circ_parallel = QuantumCircuit(len(backend_qubits), len(set(all_qubits)))
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
- raw_qc_list_parallel,
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=False,
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 qubits in self.qubit_layouts:
188
- coupling_map = set_coupling_map(qubits, self.backend, physical_layout="fixed")
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
- raw_qc_list,
207
+ composed_qc_list[idx],
191
208
  self.backend,
192
- qubits,
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
- for key, value in (self.configuration.__dict__ | self.__dict__).items():
214
- if key == "benchmark": # Avoid saving the class objects
215
- dataset.attrs[key] = value.name
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
- else:
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"Now generating {self.configuration.num_circuits} random GST circuits...")
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-4]
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
- testing: bool = False
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
- "2QXYCZ",
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, 2QXYCZ, 2QXYCZ_extended, 3QXYCZ."
455
+ "1QXYI, 2QXYICZ, 2QXYCZ_extended, 3QXYCZ."
408
456
  )
409
- if configuration.gate_set in ["1QXYI", "2QXYCZ", "2QXYCZ_extended", "3QXYCZ"]:
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, 2QXYCZ, 2QXYCZ_extended,"
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 = [RGate(1e-10, 0), RGate(0.5 * np.pi, 0), RGate(0.5 * np.pi, np.pi / 2)]
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 == "2QXYCZ":
468
- gate_qubits = [[0], [1], [0], [1], [0, 1]]
469
- gates = [QuantumCircuit(num_qubits, 0) for _ in range(5)]
470
- gates[0].append(RGate(0.5 * np.pi, 0), [0])
471
- gates[1].append(RGate(0.5 * np.pi, 0), [1])
472
- gates[2].append(RGate(0.5 * np.pi, np.pi / 2), [0])
473
- gates[3].append(RGate(0.5 * np.pi, np.pi / 2), [1])
474
- gates[4].append(CZGate(), [0, 1])
475
- gate_labels = ["Rx_pi_2", "Rx_pi_2", "Ry_pi_2", "Ry_pi_2", "cz"]
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)]