iqm-benchmarks 2.50__tar.gz → 2.52__tar.gz

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.
Files changed (112) hide show
  1. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/CHANGELOG.rst +8 -0
  2. {iqm_benchmarks-2.50/src/iqm_benchmarks.egg-info → iqm_benchmarks-2.52}/PKG-INFO +1 -1
  3. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/entanglement/ghz.py +129 -21
  4. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/optimization/qscore.py +247 -44
  5. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52/src/iqm_benchmarks.egg-info}/PKG-INFO +1 -1
  6. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_qscore.py +5 -4
  7. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/.github/workflows/main.yml +0 -0
  8. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/.github/workflows/publish.yml +0 -0
  9. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/.github/workflows/tag_and_release.yml +0 -0
  10. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/.gitignore +0 -0
  11. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/LICENSE +0 -0
  12. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/MANIFEST.in +0 -0
  13. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/README.md +0 -0
  14. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/benchmark_runner.py +0 -0
  15. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docbuild +0 -0
  16. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/API.rst +0 -0
  17. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/Makefile +0 -0
  18. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/_static/images/favicon.ico +0 -0
  19. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/_static/images/logo.png +0 -0
  20. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/_templates/autosummary-class-template.rst +0 -0
  21. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/_templates/autosummary-module-template.rst +0 -0
  22. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/changelog.rst +0 -0
  23. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/conf.py +0 -0
  24. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/development/development.rst +0 -0
  25. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/development/generate_2qubit_cliffords.ipynb +0 -0
  26. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/development/how_to_make_your_own_benchmark.ipynb +0 -0
  27. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/devices/devices.rst +0 -0
  28. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/devices/spark.ipynb +0 -0
  29. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/devices/star.ipynb +0 -0
  30. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_clifford_rb.ipynb +0 -0
  31. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_clops.ipynb +0 -0
  32. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_coherence.ipynb +0 -0
  33. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_eplg.ipynb +0 -0
  34. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_experiment_all.ipynb +0 -0
  35. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_ghz.ipynb +0 -0
  36. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_graphstate.ipynb +0 -0
  37. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_gst.ipynb +0 -0
  38. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_interleaved_rb.ipynb +0 -0
  39. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_mirror_rb.ipynb +0 -0
  40. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_qscore.ipynb +0 -0
  41. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/example_quantum_volume.ipynb +0 -0
  42. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/examples/examples.rst +0 -0
  43. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/index.rst +0 -0
  44. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/license.rst +0 -0
  45. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/docs/readme.md +0 -0
  46. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/format +0 -0
  47. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/pyproject.toml +0 -0
  48. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/requirements.txt +0 -0
  49. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/scheduled_experiments/adonis/__init__.py +0 -0
  50. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/scheduled_experiments/adonis/weekly.py +0 -0
  51. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/setup.cfg +0 -0
  52. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/__init__.py +0 -0
  53. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/benchmark.py +0 -0
  54. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/benchmark_definition.py +0 -0
  55. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/circuit_containers.py +0 -0
  56. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/coherence/__init__.py +0 -0
  57. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/coherence/coherence.py +0 -0
  58. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/compressive_gst/__init__.py +0 -0
  59. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/compressive_gst/compressive_gst.py +0 -0
  60. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/compressive_gst/gst_analysis.py +0 -0
  61. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/entanglement/__init__.py +0 -0
  62. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/entanglement/graph_states.py +0 -0
  63. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/logging_config.py +0 -0
  64. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/optimization/__init__.py +0 -0
  65. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/quantum_volume/__init__.py +0 -0
  66. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/quantum_volume/clops.py +0 -0
  67. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/quantum_volume/quantum_volume.py +0 -0
  68. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/__init__.py +0 -0
  69. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl +0 -0
  70. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl +0 -0
  71. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/clifford_rb/__init__.py +0 -0
  72. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py +0 -0
  73. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/direct_rb/__init__.py +0 -0
  74. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/direct_rb/direct_rb.py +0 -0
  75. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/eplg/__init__.py +0 -0
  76. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/eplg/eplg.py +0 -0
  77. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py +0 -0
  78. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py +0 -0
  79. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py +0 -0
  80. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py +0 -0
  81. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/multi_lmfit.py +0 -0
  82. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py +0 -0
  83. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/readout_mitigation.py +0 -0
  84. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/utils.py +0 -0
  85. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/utils_plots.py +0 -0
  86. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm/benchmarks/utils_shadows.py +0 -0
  87. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm_benchmarks.egg-info/SOURCES.txt +0 -0
  88. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm_benchmarks.egg-info/dependency_links.txt +0 -0
  89. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm_benchmarks.egg-info/requires.txt +0 -0
  90. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/iqm_benchmarks.egg-info/top_level.txt +0 -0
  91. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/LICENSE +0 -0
  92. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/README.md +0 -0
  93. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/additional_fns.py +0 -0
  94. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/algorithm.py +0 -0
  95. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/compatibility.py +0 -0
  96. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/low_level_jit.py +0 -0
  97. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/optimization.py +0 -0
  98. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/qiskit_interface.py +0 -0
  99. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/reporting/figure_gen.py +0 -0
  100. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/src/mGST/reporting/reporting.py +0 -0
  101. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tag-from-pipeline.sh +0 -0
  102. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/test +0 -0
  103. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_coherence.py +0 -0
  104. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_ghz.py +0 -0
  105. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_graph_states.py +0 -0
  106. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_gst.py +0 -0
  107. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_qv.py +0 -0
  108. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/test_rb.py +0 -0
  109. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/unit/test_backend_transpilation.py +0 -0
  110. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/unit/test_benchmark_circuit.py +0 -0
  111. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/tests/unit/test_submit_execute.py +0 -0
  112. {iqm_benchmarks-2.50 → iqm_benchmarks-2.52}/update-requirements.py +0 -0
@@ -2,6 +2,14 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 2.52
6
+ ============
7
+ * Circuit transpilation improvements for GHZ benchmark implemented.
8
+
9
+ Version 2.51
10
+ ============
11
+ * Circuit transpilation improvements for Q-score implemented.
12
+
5
13
  Version 2.50
6
14
  ============
7
15
  * Update iqm-client and iqm-station-control-client dependency versions as part of IQM OS 4.3 release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iqm-benchmarks
3
- Version: 2.50
3
+ Version: 2.52
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
@@ -25,7 +25,7 @@ import matplotlib.pyplot as plt
25
25
  import networkx
26
26
  from networkx import Graph, all_pairs_shortest_path, is_connected, minimum_spanning_tree
27
27
  import numpy as np
28
- from qiskit import QuantumRegister
28
+ from qiskit import ClassicalRegister, QuantumRegister
29
29
  from qiskit.quantum_info import random_clifford
30
30
  from qiskit.transpiler import CouplingMap
31
31
  from qiskit_aer import Aer
@@ -55,6 +55,7 @@ from iqm.benchmarks.utils import (
55
55
  xrvariable_to_counts,
56
56
  )
57
57
  from iqm.qiskit_iqm import IQMCircuit as QuantumCircuit
58
+ from iqm.qiskit_iqm import transpile_to_IQM
58
59
  from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
59
60
 
60
61
 
@@ -304,6 +305,74 @@ def generate_ghz_log_cruz(num_qubits: int) -> QuantumCircuit:
304
305
  return qc
305
306
 
306
307
 
308
+ ## can introduce another fucntion to time order here CZ and pick the best MOVE qubit.
309
+
310
+
311
+ def generate_ghz_star_optimal(
312
+ qubit_layout: List[int], cal_url: str, backend: IQMBackendBase, inv: bool = False
313
+ ) -> QuantumCircuit:
314
+ """
315
+ Generates the circuit for creating a GHZ state by maximizing the number of CZ gates between a pair of MOVE gates.
316
+
317
+ Args:
318
+ qubit_layout: List[int]
319
+ The layout of qubits for the GHZ state.
320
+ cal_url: str
321
+ The calibration URL for extracting fidelities.
322
+ backend: IQMBackendBase
323
+ The backend to be used for the quantum circuit.
324
+ inv: bool
325
+ Whether to generate the inverse circuit.
326
+
327
+ Returns:
328
+ QuantumCircuit: A quantum circuit generating a GHZ state on a given number of qubits.
329
+ """
330
+ num_qubits = len(qubit_layout)
331
+
332
+ # Initialize quantum and classical registers
333
+ comp_r = QuantumRegister(1, "comp_r") # Computational resonator
334
+ q = QuantumRegister(backend.num_qubits, "q") # Qubits
335
+ c = ClassicalRegister(num_qubits, "c")
336
+ qc = QuantumCircuit(comp_r, q, c, name="GHZ_star_optimal")
337
+
338
+ cal_data = extract_fidelities(cal_url, all_metrics=True)
339
+ # Determine the best move qubit
340
+ move_dict = {q + 1: cal_data[1][q] for q in qubit_layout} ## +1 to match qubit indexing in cal data
341
+ best_move = max(move_dict, key=move_dict.get)
342
+
343
+ T2 = cal_data[-1]["t2_echo_time"]
344
+ t2_dict = {qubit + 1: T2[qubit + 1] for qubit in qubit_layout} ## +1 to match qubit indexing in cal data
345
+ cz_order = dict(sorted(t2_dict.items(), key=lambda item: item[1], reverse=True))
346
+ qubits_to_measure = list(cz_order.keys())
347
+ cz_order.pop(best_move)
348
+
349
+ # Construct the quantum circuit
350
+ qc.h(best_move)
351
+ qc.move(best_move, 0)
352
+ for qubit in cz_order.keys():
353
+ qc.cx(0, qubit)
354
+ qc.barrier()
355
+ qc.move(best_move, 0)
356
+ qc.barrier()
357
+ qc.measure(sorted(qubits_to_measure), list(range(num_qubits)))
358
+
359
+ if inv:
360
+ comp_r = QuantumRegister(1, "comp_r") # Computational resonator
361
+ q = QuantumRegister(backend.num_qubits, "q") # Qubits
362
+ c = ClassicalRegister(num_qubits, "c")
363
+ qc = QuantumCircuit(comp_r, q, c, name="GHZ_star_optimal_inv")
364
+
365
+ qc.move(best_move, 0)
366
+ for qubit in reversed(cz_order.keys()):
367
+ qc.cx(0, qubit)
368
+ qc.barrier()
369
+ qc.move(best_move, 0)
370
+ qc.h(best_move)
371
+ qc.barrier()
372
+
373
+ return qc
374
+
375
+
307
376
  def generate_ghz_star(num_qubits: int) -> QuantumCircuit:
308
377
  """
309
378
  Generates the circuit for creating a GHZ state by maximizing the number of CZ gates between a pair of MOVE gates.
@@ -648,7 +717,20 @@ class GHZBenchmark(Benchmark):
648
717
  optimize_sqg=self.optimize_sqg,
649
718
  )
650
719
  final_ghz = ghz_native_transpiled
651
-
720
+ elif routine == "star_optimal":
721
+ if self.cal_url is None:
722
+ raise ValueError("Calibration URL must be provided for 'star_optimal' routine.")
723
+ ghz = generate_ghz_star_optimal(qubit_layout, self.cal_url, self.backend)
724
+ circuit_group.add_circuit(ghz)
725
+ ghz_native_transpiled = transpile_to_IQM(
726
+ ghz,
727
+ self.backend,
728
+ existing_moves_handling=True,
729
+ perform_move_routing=False,
730
+ optimize_single_qubits=self.optimize_sqg,
731
+ optimization_level=self.qiskit_optim_level,
732
+ )
733
+ final_ghz = [ghz_native_transpiled]
652
734
  else:
653
735
  ghz_log = [generate_ghz_log_cruz(qubit_count), generate_ghz_log_mooney(qubit_count)]
654
736
  ghz_native_transpiled, _ = perform_backend_transpilation(
@@ -691,27 +773,54 @@ class GHZBenchmark(Benchmark):
691
773
 
692
774
  qc = qc_list[0].copy()
693
775
  qc.remove_final_measurements()
694
- qc_inv = qc.inverse()
695
776
  phases = [np.pi * i / (qubit_count + 1) for i in range(2 * qubit_count + 2)]
696
- for phase in phases:
697
- qc_phase = qc.copy()
698
- qc_phase.barrier()
699
- for qubit, _ in enumerate(qubit_layout):
700
- qc_phase.p(phase, qubit)
701
- qc_phase.barrier()
702
- qc_phase.compose(qc_inv, inplace=True)
703
- qc_phase.measure_active()
704
- qc_list.append(qc_phase)
777
+ if self.state_generation_routine == "star_optimal":
778
+ qc_inv = generate_ghz_star_optimal(qubit_layout, self.cal_url, self.backend, inv=True)
779
+ for phase in phases:
780
+ qc_phase = qc.copy()
781
+ qc_phase.barrier()
782
+ for _, qubit in enumerate(qubit_layout):
783
+ qc_phase.p(phase, qubit + 1)
784
+ qc_phase.barrier()
785
+ qc_phase.compose(qc_inv, inplace=True)
786
+ qc_phase.measure([q + 1 for q in qubit_layout], list(range(qubit_count)))
787
+ qc_list.append(qc_phase)
788
+ else:
789
+ qc_inv = qc.inverse()
790
+ for phase in phases:
791
+ qc_phase = qc.copy()
792
+ qc_phase.barrier()
793
+ for qubit, _ in enumerate(qubit_layout):
794
+ qc_phase.p(phase, qubit)
795
+ qc_phase.barrier()
796
+ qc_phase.compose(qc_inv, inplace=True)
797
+ qc_phase.measure_active()
798
+ qc_list.append(qc_phase)
705
799
 
706
800
  fixed_coupling_map = set_coupling_map(qubit_layout, self.backend, "fixed")
707
- qc_list_transpiled, _ = perform_backend_transpilation(
708
- qc_list,
709
- self.backend,
710
- qubit_layout,
711
- fixed_coupling_map,
712
- qiskit_optim_level=self.qiskit_optim_level,
713
- optimize_sqg=self.optimize_sqg,
714
- )
801
+
802
+ if self.state_generation_routine == "star_optimal":
803
+ qc_list_transpiled = [
804
+ transpile_to_IQM(
805
+ ghz,
806
+ self.backend,
807
+ existing_moves_handling=True,
808
+ perform_move_routing=False,
809
+ optimize_single_qubits=self.optimize_sqg,
810
+ optimization_level=self.qiskit_optim_level,
811
+ )
812
+ for ghz in qc_list
813
+ ]
814
+
815
+ else:
816
+ qc_list_transpiled, _ = perform_backend_transpilation(
817
+ qc_list,
818
+ self.backend,
819
+ qubit_layout,
820
+ fixed_coupling_map,
821
+ qiskit_optim_level=self.qiskit_optim_level,
822
+ optimize_sqg=self.optimize_sqg,
823
+ )
715
824
  circuit_group = CircuitGroup(name=idx, circuits=qc_list)
716
825
  self.circuits["untranspiled_circuits"].circuit_groups.append(circuit_group)
717
826
  return qc_list_transpiled
@@ -791,7 +900,6 @@ class GHZBenchmark(Benchmark):
791
900
  transpiled_ghz_group: CircuitGroup = self.generate_native_ghz(
792
901
  qubit_layout, qubit_count, self.state_generation_routine
793
902
  )
794
-
795
903
  match self.fidelity_routine:
796
904
  case "randomized_measurements":
797
905
  all_circuits_list, _ = self.append_rms(cast(int, self.num_RMs), qubit_layout)
@@ -1,5 +1,6 @@
1
+ # pylint: disable=too-many-lines
1
2
  """
2
- Qscore benchmark
3
+ This module contains functions and classes for the Qscore benchmarking process.
3
4
  """
4
5
 
5
6
  import itertools
@@ -12,7 +13,8 @@ import matplotlib.pyplot as plt
12
13
  from networkx import Graph
13
14
  import networkx as nx
14
15
  import numpy as np
15
- from qiskit import QuantumCircuit
16
+ from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
17
+ from qiskit.circuit.library import RZZGate
16
18
  from scipy.optimize import basinhopping, minimize
17
19
  import xarray as xr
18
20
 
@@ -34,6 +36,8 @@ from iqm.benchmarks.utils import ( # execute_with_dd,
34
36
  submit_execute,
35
37
  xrvariable_to_counts,
36
38
  )
39
+ from iqm.iqm_client.transpile import ExistingMoveHandlingOptions
40
+ from iqm.qiskit_iqm import IQMCircuit, transpile_to_IQM
37
41
  from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
38
42
 
39
43
 
@@ -195,7 +199,7 @@ def get_optimal_angles(num_layers: int) -> List[float]:
195
199
  # "The fixed angle conjecture for QAOA on regular MaxCut graphs."
196
200
  # arXiv preprint arXiv:2107.00677 (2021).
197
201
 
198
- OPTIMAL_INITIAL_ANGLES = {
202
+ OPTIMAL_INITIAL_ANGLES = { # pylint: disable=undefined-variable
199
203
  "1": [-0.616, 0.393 / 2],
200
204
  "2": [-0.488, 0.898 / 2, 0.555 / 2, 0.293 / 2],
201
205
  "3": [-0.422, 0.798 / 2, 0.937 / 2, 0.609 / 2, 0.459 / 2, 0.235 / 2],
@@ -323,6 +327,76 @@ def get_optimal_angles(num_layers: int) -> List[float]:
323
327
  return OPTIMAL_INITIAL_ANGLES[str(num_layers)]
324
328
 
325
329
 
330
+ def group_rzz_gates(circuit: QuantumCircuit):
331
+ """
332
+ Extract and group commuting RZZ gates so that gates sharing a qubit
333
+ are placed next to each other.
334
+
335
+ Args:
336
+ circuit: QuantumCircuit containing RZZ gates.
337
+
338
+ Returns:
339
+ grouped_rzz: list of tuples (instr, [q0_idx, q1_idx])
340
+ in reordered grouping.
341
+ """
342
+ # Collect RZZ gates as (gate_pos, instr, [q0, q1])
343
+ rzz_entries = []
344
+ for pos, (instr, qargs, _) in enumerate(circuit.data):
345
+ if isinstance(instr, RZZGate) or getattr(instr, "name", "") == "rzz":
346
+ qidxs = [circuit.find_bit(q).index for q in qargs]
347
+ rzz_entries.append((pos, instr, qidxs))
348
+
349
+ if not rzz_entries:
350
+ return []
351
+
352
+ # Build bipartite graph: gate nodes <-> qubit nodes
353
+ B = nx.Graph()
354
+ for gate_pos, instr, qidxs in rzz_entries:
355
+ gnode = f"g{gate_pos}"
356
+ B.add_node(gnode, kind="gate", instr=instr, qidxs=qidxs)
357
+ for q in qidxs:
358
+ qnode = f"q{q}"
359
+ B.add_node(qnode, kind="qubit", qidx=q)
360
+ B.add_edge(gnode, qnode)
361
+
362
+ grouped_rzz = []
363
+
364
+ # Repeatedly pick the hub qubit with the most RZZs
365
+ while True:
366
+ qubit_nodes = [n for n, d in B.nodes(data=True) if d["kind"] == "qubit"]
367
+ if not qubit_nodes:
368
+ break
369
+
370
+ # Pick hub: max degree, tie-break by smallest index
371
+ hub = max(qubit_nodes, key=lambda q: (B.degree(q), -int(q[1:])))
372
+ hub_idx = int(hub[1:])
373
+
374
+ # Gates connected to hub
375
+ connected_gates = [nbr for nbr in B.neighbors(hub) if nbr.startswith("g")]
376
+ if not connected_gates:
377
+ B.remove_node(hub)
378
+ continue
379
+
380
+ # Sort gates: prioritize partner qubits with higher degree
381
+ def sort_key(g):
382
+ qidxs = B.nodes[g]["qidxs"]
383
+ other = [q for q in qidxs if q != hub_idx][0]
384
+ return (-B.degree(f"q{other}"), other)
385
+
386
+ connected_gates.sort(key=sort_key)
387
+
388
+ # Append in chosen order
389
+ for g in connected_gates:
390
+ data = B.nodes[g]
391
+ grouped_rzz.append((data["qidxs"]))
392
+ B.remove_node(g)
393
+
394
+ # Remove hub itself
395
+ B.remove_node(hub)
396
+
397
+ return grouped_rzz
398
+
399
+
326
400
  def plot_approximation_ratios(
327
401
  nodes: list[int],
328
402
  beta_ratio: list[float],
@@ -369,7 +443,7 @@ def plot_approximation_ratios(
369
443
  ax.set_ylabel(r"Q-score ratio $\beta(n)$")
370
444
  ax.set_xlabel("Number of nodes $(n)$")
371
445
  plt.xticks(range(min(nodes), max(nodes) + 1))
372
- plt.legend(loc="lower right")
446
+ plt.legend(loc="upper right")
373
447
  plt.grid(True)
374
448
 
375
449
  if use_virtual_node and use_classically_optimized_angles:
@@ -600,6 +674,7 @@ class QScoreBenchmark(Benchmark):
600
674
  self.session_timestamp = strftime("%Y%m%d-%H%M%S")
601
675
  self.execution_timestamp = ""
602
676
  self.seed = configuration.seed
677
+ self.num_trials = configuration.num_trials
603
678
 
604
679
  self.graph_physical: Graph
605
680
  self.virtual_nodes: List[Tuple[int, int]]
@@ -622,10 +697,107 @@ class QScoreBenchmark(Benchmark):
622
697
  list(x) for x in cast(Sequence[Sequence[int]], configuration.custom_qubits_array)
623
698
  ]
624
699
 
700
+ def greedy_vertex_cover_with_mapping(self, G: nx.Graph):
701
+ """
702
+ Approximate a minimum vertex cover for a given graph, providing a mapping of nodes to the edges they cover.
703
+
704
+ Args:
705
+ G (nx.Graph): The input graph for which the vertex cover is to be computed.
706
+
707
+ Returns:
708
+ dict: A dictionary where keys are nodes and values are lists of edges that each node covers.
709
+ """
710
+
711
+ G = G.copy()
712
+ cover_map = {}
713
+
714
+ while G.number_of_edges() > 0:
715
+ # Pick node with max degree
716
+ node = max(G.degree, key=lambda x: x[1])[0]
717
+
718
+ # Collect neighbors (unique)
719
+ neighbors = list(G.neighbors(node))
720
+ cover_map[node] = neighbors
721
+
722
+ # Remove node (and incident edges)
723
+ G.remove_node(node)
724
+
725
+ return cover_map
726
+
727
+ def generate_maxcut_ansatz_star( # pylint: disable=too-many-branches
728
+ self,
729
+ graph,
730
+ theta,
731
+ ):
732
+ """Generate an ansatz circuit for QAOA MaxCut, with measurements at the end.
733
+
734
+ Args:
735
+ graph (networkx graph): the MaxCut problem graph
736
+ theta (list[float]): the variational parameters for QAOA, first gammas then betas
737
+
738
+ Returns:
739
+ QuantumCircuit: the QAOA ansatz quantum circuit.
740
+ """
741
+
742
+ gamma = theta[: self.num_qaoa_layers]
743
+ beta = theta[self.num_qaoa_layers :]
744
+
745
+ if self.graph_physical.number_of_nodes() != graph.number_of_nodes():
746
+ num_qubits = self.graph_physical.number_of_nodes()
747
+ # re-label the nodes to be between 0 and _num_qubits
748
+ self.node_to_qubit = {node: qubit for qubit, node in enumerate(list(self.graph_physical.nodes))}
749
+ self.qubit_to_node = dict(enumerate(list(self.graph_physical.nodes)))
750
+ else:
751
+ num_qubits = graph.number_of_nodes()
752
+ self.node_to_qubit = {node: node for node in list(self.graph_physical.nodes)} # no relabeling
753
+ self.qubit_to_node = self.node_to_qubit
754
+
755
+ covermap = self.greedy_vertex_cover_with_mapping(self.graph_physical)
756
+ new_covermap = {}
757
+ for key, value in covermap.items():
758
+ new_covermap[self.node_to_qubit[key]] = [self.node_to_qubit[i] for i in value]
759
+ covermap = new_covermap
760
+
761
+ compr = QuantumRegister(1, "compr")
762
+ q = QuantumRegister(num_qubits, "q")
763
+ c = ClassicalRegister(num_qubits, "c")
764
+ qaoa_qc = IQMCircuit(compr, q, c) # num_qb+1,num_qb)
765
+ # in case the graph is trivial: return empty circuit
766
+ if num_qubits == 0:
767
+ return QuantumCircuit(1)
768
+ for i in range(1, num_qubits + 1):
769
+ qaoa_qc.h(i)
770
+ for layer in range(self.num_qaoa_layers):
771
+ for move_qubit, edge_qubits in covermap.items():
772
+ qaoa_qc.move(move_qubit + 1, 0)
773
+ for edge_qubit in edge_qubits:
774
+ qaoa_qc.rzz(2 * gamma[layer], 0, edge_qubit + 1)
775
+ qaoa_qc.move(move_qubit + 1, 0)
776
+
777
+ # include edges of the virtual node as rz terms
778
+ for vn in self.virtual_nodes:
779
+ for edge in graph.edges(vn[0]):
780
+ # exclude edges between virtual nodes
781
+ edges_between_virtual_nodes = list(itertools.combinations([i[0] for i in self.virtual_nodes], 2))
782
+ if set(edge) not in list(map(set, edges_between_virtual_nodes)):
783
+ # The value of the fixed node defines the sign of the rz gate
784
+ sign = 1.0
785
+ if vn[1] == 1:
786
+ sign = -1.0
787
+ qaoa_qc.rz(sign * 2.0 * gamma[layer], self.node_to_qubit[edge[1]] + 1)
788
+
789
+ for i in range(1, num_qubits + 1):
790
+ qaoa_qc.rx(2 * beta[layer], i)
791
+ qaoa_qc.barrier()
792
+ qaoa_qc.measure(q, c)
793
+
794
+ return qaoa_qc
795
+
625
796
  def generate_maxcut_ansatz( # pylint: disable=too-many-branches
626
797
  self,
627
798
  graph: Graph,
628
799
  theta: list[float],
800
+ rzz_list=None,
629
801
  ) -> QuantumCircuit:
630
802
  """Generate an ansatz circuit for QAOA MaxCut, with measurements at the end.
631
803
 
@@ -656,10 +828,14 @@ class QScoreBenchmark(Benchmark):
656
828
  for i in range(0, num_qubits):
657
829
  qaoa_qc.h(i)
658
830
  for layer in range(self.num_qaoa_layers):
659
- for edge in self.graph_physical.edges():
660
- i = self.node_to_qubit[edge[0]]
661
- j = self.node_to_qubit[edge[1]]
662
- qaoa_qc.rzz(2 * gamma[layer], i, j)
831
+ if rzz_list is not None and layer == 0:
832
+ for rzzs in rzz_list:
833
+ qaoa_qc.rzz(2 * gamma[layer], rzzs[0], rzzs[1])
834
+ else:
835
+ for edge in self.graph_physical.edges():
836
+ i = self.node_to_qubit[edge[0]]
837
+ j = self.node_to_qubit[edge[1]]
838
+ qaoa_qc.rzz(2 * gamma[layer], i, j)
663
839
 
664
840
  # include edges of the virtual node as rz terms
665
841
  for vn in self.virtual_nodes:
@@ -792,6 +968,33 @@ class QScoreBenchmark(Benchmark):
792
968
  no_edge_instances = []
793
969
  qc_all = [] # all circuits, including those with no edges
794
970
  start_seed = seed
971
+
972
+ # Choose the qubit layout
973
+ if self.choose_qubits_routine.lower() == "naive":
974
+ qubit_set = self.choose_qubits_naive(updated_num_nodes)
975
+ elif self.choose_qubits_routine.lower() == "custom" or self.choose_qubits_routine.lower() == "mapomatic":
976
+ qubit_set = self.choose_qubits_custom(updated_num_nodes)
977
+ else:
978
+ raise ValueError('choose_qubits_routine must either be "naive" or "custom".')
979
+ qubit_set_list.append(qubit_set)
980
+
981
+ qcvv_logger.setLevel(logging.WARNING)
982
+ if self.choose_qubits_routine == "naive":
983
+ active_qubit_set = None
984
+ effective_coupling_map = self.backend.coupling_map
985
+ else:
986
+ active_qubit_set = qubit_set
987
+ effective_coupling_map = self.backend.coupling_map.reduce(active_qubit_set)
988
+
989
+ transpilation_params = {
990
+ "backend": self.backend,
991
+ "qubits": active_qubit_set,
992
+ "coupling_map": effective_coupling_map,
993
+ "qiskit_optim_level": self.qiskit_optim_level,
994
+ "optimize_sqg": self.optimize_sqg,
995
+ "routing_method": self.routing_method,
996
+ }
997
+
795
998
  for instance in range(self.num_instances):
796
999
  qcvv_logger.debug(f"Executing graph {instance} with {num_nodes} nodes.")
797
1000
  graph = nx.generators.erdos_renyi_graph(num_nodes, 0.5, seed=seed)
@@ -820,17 +1023,6 @@ class QScoreBenchmark(Benchmark):
820
1023
  no_edge_instances.append(instance)
821
1024
  qcvv_logger.debug(f"Graph {instance+1}/{self.num_instances} had no edges: cut size = 0.")
822
1025
 
823
- # Choose the qubit layout
824
- if self.choose_qubits_routine.lower() == "naive":
825
- qubit_set = self.choose_qubits_naive(updated_num_nodes)
826
- elif (
827
- self.choose_qubits_routine.lower() == "custom" or self.choose_qubits_routine.lower() == "mapomatic"
828
- ):
829
- qubit_set = self.choose_qubits_custom(updated_num_nodes)
830
- else:
831
- raise ValueError('choose_qubits_routine must either be "naive" or "custom".')
832
- qubit_set_list.append(qubit_set)
833
-
834
1026
  if self.use_classically_optimized_angles:
835
1027
  if graph.number_of_edges() != 0:
836
1028
  theta = calculate_optimal_angles_for_QAOA_p1(graph)
@@ -841,11 +1033,26 @@ class QScoreBenchmark(Benchmark):
841
1033
 
842
1034
  theta_list.append(theta)
843
1035
 
844
- qc = self.generate_maxcut_ansatz(graph, theta)
845
-
846
- if len(qc.count_ops()) != 0:
847
- qc_list.append(qc)
848
- qc_all.append(qc)
1036
+ if self.backend.has_resonators():
1037
+ qc_opt = self.generate_maxcut_ansatz_star(graph, theta)
1038
+ else:
1039
+ qc_list_temp = []
1040
+ cz_count_temp = []
1041
+ for _ in range(self.num_trials):
1042
+ perm = np.random.permutation(num_nodes)
1043
+ mapping = dict(zip(graph.nodes, perm))
1044
+ G1_permuted = nx.relabel_nodes(graph, mapping)
1045
+ theta = calculate_optimal_angles_for_QAOA_p1(G1_permuted)
1046
+ qc_perm = self.generate_maxcut_ansatz(G1_permuted, theta)
1047
+ transpiled_qc_temp, _ = perform_backend_transpilation([qc_perm], **transpilation_params)
1048
+ cz_count_temp.append(transpiled_qc_temp[0].count_ops().get("cz", 0))
1049
+ qc_list_temp.append(qc_perm)
1050
+ min_cz_index = cz_count_temp.index(min(cz_count_temp))
1051
+ qc_opt = qc_list_temp[min_cz_index]
1052
+
1053
+ if len(qc_opt.count_ops()) != 0:
1054
+ qc_list.append(qc_opt)
1055
+ qc_all.append(qc_opt)
849
1056
  qubit_to_node_copy = self.qubit_to_node.copy()
850
1057
  qubit_to_node_list.append(qubit_to_node_copy)
851
1058
  else:
@@ -855,24 +1062,20 @@ class QScoreBenchmark(Benchmark):
855
1062
  seed += 1
856
1063
  qcvv_logger.debug(f"Solved the MaxCut on graph {instance+1}/{self.num_instances}.")
857
1064
 
858
- qcvv_logger.setLevel(logging.WARNING)
859
- if self.choose_qubits_routine == "naive":
860
- active_qubit_set = None
861
- effective_coupling_map = self.backend.coupling_map
1065
+ if self.backend.has_resonators():
1066
+ transpiled_qc = [
1067
+ transpile_to_IQM(
1068
+ qc,
1069
+ self.backend,
1070
+ optimize_single_qubits=self.optimize_sqg,
1071
+ existing_moves_handling=ExistingMoveHandlingOptions.KEEP,
1072
+ perform_move_routing=False,
1073
+ optimization_level=self.qiskit_optim_level,
1074
+ )
1075
+ for qc in qc_list
1076
+ ]
862
1077
  else:
863
- active_qubit_set = qubit_set
864
- effective_coupling_map = self.backend.coupling_map.reduce(active_qubit_set)
865
-
866
- transpilation_params = {
867
- "backend": self.backend,
868
- "qubits": active_qubit_set,
869
- "coupling_map": effective_coupling_map,
870
- "qiskit_optim_level": self.qiskit_optim_level,
871
- "optimize_sqg": self.optimize_sqg,
872
- "routing_method": self.routing_method,
873
- }
874
-
875
- transpiled_qc, _ = perform_backend_transpilation(qc_list, **transpilation_params)
1078
+ transpiled_qc, _ = perform_backend_transpilation(qc_list, **transpilation_params)
876
1079
 
877
1080
  sorted_transpiled_qc_list = {tuple(qubit_set): transpiled_qc}
878
1081
  # Execute on the backend
@@ -892,9 +1095,7 @@ class QScoreBenchmark(Benchmark):
892
1095
  num_instances_with_edges = len(instance_with_edges)
893
1096
  if self.REM:
894
1097
  counts_retrieved, time_retrieve = retrieve_all_counts(jobs)
895
- rem_counts = apply_readout_error_mitigation(
896
- backend, transpiled_qc, counts_retrieved, self.mit_shots
897
- )
1098
+ rem_counts = apply_readout_error_mitigation(backend, transpiled_qc, counts_retrieved, self.mit_shots)
898
1099
  execution_results.extend(
899
1100
  rem_counts[0][instance].nearest_probability_distribution()
900
1101
  for instance in range(num_instances_with_edges)
@@ -960,6 +1161,7 @@ class QScoreConfiguration(BenchmarkConfigurationBase):
960
1161
  * Default is 3.
961
1162
  optimize_sqg (bool): Whether Single Qubit Gate Optimization is performed upon transpilation.
962
1163
  * Default is True.
1164
+ num_trials (Optional[int]): Number of trials to perform when choosing graph permutations to minimize CZ gates.
963
1165
  seed (int): The random seed.
964
1166
  * Default is 1.
965
1167
  REM (bool): Use readout error mitigation.
@@ -980,6 +1182,7 @@ class QScoreConfiguration(BenchmarkConfigurationBase):
980
1182
  custom_qubits_array: Optional[Sequence[Sequence[int]]] = None
981
1183
  qiskit_optim_level: int = 3
982
1184
  optimize_sqg: bool = True
1185
+ num_trials: int = 10
983
1186
  seed: int = 1
984
1187
  REM: bool = False
985
1188
  mit_shots: int = 1000
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iqm-benchmarks
3
- Version: 2.50
3
+ Version: 2.52
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
@@ -7,7 +7,7 @@ from iqm.qiskit_iqm.fake_backends.fake_deneb import IQMFakeDeneb
7
7
 
8
8
  class TestQScore:
9
9
  backend = IQMFakeApollo()
10
- custom_qubits_array = [[0, 1, 3], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
10
+ custom_qubits_array = [[0, 1, 2, 3], [0, 1, 2, 3, 4]]
11
11
 
12
12
  def test_qscore(self):
13
13
  EXAMPLE_QSCORE = QScoreConfiguration(
@@ -15,13 +15,14 @@ class TestQScore:
15
15
  num_qaoa_layers=1,
16
16
  shots=4,
17
17
  calset_id=None, # calibration set ID, default is None
18
- min_num_nodes=3,
18
+ min_num_nodes=4,
19
19
  max_num_nodes=5,
20
20
  use_virtual_node=True,
21
21
  use_classically_optimized_angles=True,
22
22
  choose_qubits_routine="custom",
23
23
  custom_qubits_array=self.custom_qubits_array,
24
- seed=1,
24
+ seed=200,
25
+ num_trials = 2,
25
26
  REM=True,
26
27
  mit_shots=10,
27
28
  )
@@ -32,4 +33,4 @@ class TestQScore:
32
33
 
33
34
  class TestQScoreDeneb(TestQScore):
34
35
  backend = IQMFakeDeneb()
35
- custom_qubits_array = [[1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
36
+ custom_qubits_array = [[1, 2, 3, 4], [1, 2, 3, 4, 5]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes