qiskit 2.0.1__cp39-abi3-macosx_11_0_arm64.whl → 2.1.0rc1__cp39-abi3-macosx_11_0_arm64.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.
Files changed (180) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +19 -1
  3. qiskit/_accelerate.abi3.so +0 -0
  4. qiskit/circuit/__init__.py +13 -21
  5. qiskit/circuit/_add_control.py +57 -31
  6. qiskit/circuit/_classical_resource_map.py +4 -0
  7. qiskit/circuit/annotation.py +404 -0
  8. qiskit/circuit/classical/expr/__init__.py +1 -1
  9. qiskit/circuit/classical/expr/expr.py +104 -446
  10. qiskit/circuit/classical/expr/visitors.py +6 -0
  11. qiskit/circuit/classical/types/types.py +7 -130
  12. qiskit/circuit/controlflow/box.py +32 -7
  13. qiskit/circuit/delay.py +11 -9
  14. qiskit/circuit/library/arithmetic/adders/adder.py +5 -5
  15. qiskit/circuit/library/arithmetic/multipliers/multiplier.py +3 -3
  16. qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +7 -3
  17. qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py +23 -15
  18. qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py +22 -14
  19. qiskit/circuit/library/arithmetic/quadratic_form.py +6 -0
  20. qiskit/circuit/library/arithmetic/weighted_adder.py +43 -24
  21. qiskit/circuit/library/basis_change/qft.py +2 -2
  22. qiskit/circuit/library/blueprintcircuit.py +6 -0
  23. qiskit/circuit/library/boolean_logic/inner_product.py +2 -2
  24. qiskit/circuit/library/boolean_logic/quantum_and.py +2 -2
  25. qiskit/circuit/library/boolean_logic/quantum_or.py +5 -5
  26. qiskit/circuit/library/boolean_logic/quantum_xor.py +2 -2
  27. qiskit/circuit/library/data_preparation/_z_feature_map.py +2 -2
  28. qiskit/circuit/library/data_preparation/_zz_feature_map.py +2 -2
  29. qiskit/circuit/library/data_preparation/pauli_feature_map.py +2 -2
  30. qiskit/circuit/library/fourier_checking.py +2 -2
  31. qiskit/circuit/library/generalized_gates/diagonal.py +5 -1
  32. qiskit/circuit/library/generalized_gates/gms.py +5 -1
  33. qiskit/circuit/library/generalized_gates/linear_function.py +2 -2
  34. qiskit/circuit/library/generalized_gates/permutation.py +5 -1
  35. qiskit/circuit/library/generalized_gates/uc.py +1 -1
  36. qiskit/circuit/library/generalized_gates/unitary.py +21 -2
  37. qiskit/circuit/library/graph_state.py +2 -2
  38. qiskit/circuit/library/grover_operator.py +2 -2
  39. qiskit/circuit/library/hidden_linear_function.py +2 -2
  40. qiskit/circuit/library/iqp.py +2 -2
  41. qiskit/circuit/library/n_local/efficient_su2.py +2 -2
  42. qiskit/circuit/library/n_local/evolved_operator_ansatz.py +4 -2
  43. qiskit/circuit/library/n_local/excitation_preserving.py +7 -9
  44. qiskit/circuit/library/n_local/n_local.py +4 -3
  45. qiskit/circuit/library/n_local/pauli_two_design.py +2 -2
  46. qiskit/circuit/library/n_local/real_amplitudes.py +2 -2
  47. qiskit/circuit/library/n_local/two_local.py +2 -2
  48. qiskit/circuit/library/overlap.py +2 -2
  49. qiskit/circuit/library/pauli_evolution.py +3 -2
  50. qiskit/circuit/library/phase_estimation.py +2 -2
  51. qiskit/circuit/library/standard_gates/dcx.py +11 -12
  52. qiskit/circuit/library/standard_gates/ecr.py +21 -24
  53. qiskit/circuit/library/standard_gates/equivalence_library.py +232 -96
  54. qiskit/circuit/library/standard_gates/global_phase.py +5 -6
  55. qiskit/circuit/library/standard_gates/h.py +22 -45
  56. qiskit/circuit/library/standard_gates/i.py +1 -1
  57. qiskit/circuit/library/standard_gates/iswap.py +13 -31
  58. qiskit/circuit/library/standard_gates/p.py +19 -26
  59. qiskit/circuit/library/standard_gates/r.py +11 -17
  60. qiskit/circuit/library/standard_gates/rx.py +21 -45
  61. qiskit/circuit/library/standard_gates/rxx.py +7 -22
  62. qiskit/circuit/library/standard_gates/ry.py +21 -39
  63. qiskit/circuit/library/standard_gates/ryy.py +13 -28
  64. qiskit/circuit/library/standard_gates/rz.py +18 -35
  65. qiskit/circuit/library/standard_gates/rzx.py +7 -22
  66. qiskit/circuit/library/standard_gates/rzz.py +7 -19
  67. qiskit/circuit/library/standard_gates/s.py +44 -39
  68. qiskit/circuit/library/standard_gates/swap.py +25 -38
  69. qiskit/circuit/library/standard_gates/sx.py +34 -41
  70. qiskit/circuit/library/standard_gates/t.py +18 -27
  71. qiskit/circuit/library/standard_gates/u.py +8 -24
  72. qiskit/circuit/library/standard_gates/u1.py +28 -52
  73. qiskit/circuit/library/standard_gates/u2.py +9 -9
  74. qiskit/circuit/library/standard_gates/u3.py +24 -40
  75. qiskit/circuit/library/standard_gates/x.py +190 -336
  76. qiskit/circuit/library/standard_gates/xx_minus_yy.py +12 -50
  77. qiskit/circuit/library/standard_gates/xx_plus_yy.py +13 -52
  78. qiskit/circuit/library/standard_gates/y.py +19 -23
  79. qiskit/circuit/library/standard_gates/z.py +31 -38
  80. qiskit/circuit/parameter.py +14 -5
  81. qiskit/circuit/parameterexpression.py +109 -75
  82. qiskit/circuit/quantumcircuit.py +168 -98
  83. qiskit/circuit/quantumcircuitdata.py +1 -0
  84. qiskit/circuit/random/__init__.py +37 -2
  85. qiskit/circuit/random/utils.py +445 -56
  86. qiskit/circuit/tools/pi_check.py +5 -13
  87. qiskit/compiler/transpiler.py +1 -1
  88. qiskit/converters/circuit_to_instruction.py +2 -2
  89. qiskit/dagcircuit/dagnode.py +8 -3
  90. qiskit/primitives/__init__.py +2 -2
  91. qiskit/primitives/base/base_estimator.py +2 -2
  92. qiskit/primitives/containers/data_bin.py +0 -3
  93. qiskit/primitives/containers/observables_array.py +192 -108
  94. qiskit/primitives/primitive_job.py +29 -10
  95. qiskit/providers/fake_provider/generic_backend_v2.py +2 -0
  96. qiskit/qasm3/__init__.py +106 -12
  97. qiskit/qasm3/ast.py +15 -1
  98. qiskit/qasm3/exporter.py +59 -36
  99. qiskit/qasm3/printer.py +12 -0
  100. qiskit/qpy/__init__.py +183 -7
  101. qiskit/qpy/binary_io/circuits.py +256 -24
  102. qiskit/qpy/binary_io/parse_sympy_repr.py +5 -0
  103. qiskit/qpy/binary_io/schedules.py +12 -32
  104. qiskit/qpy/binary_io/value.py +36 -18
  105. qiskit/qpy/common.py +11 -3
  106. qiskit/qpy/formats.py +17 -1
  107. qiskit/qpy/interface.py +52 -12
  108. qiskit/qpy/type_keys.py +7 -1
  109. qiskit/quantum_info/__init__.py +10 -0
  110. qiskit/quantum_info/operators/__init__.py +1 -0
  111. qiskit/quantum_info/operators/symplectic/__init__.py +1 -0
  112. qiskit/quantum_info/operators/symplectic/clifford_circuits.py +26 -0
  113. qiskit/quantum_info/operators/symplectic/pauli.py +2 -2
  114. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +1 -1
  115. qiskit/result/sampled_expval.py +3 -1
  116. qiskit/synthesis/__init__.py +10 -0
  117. qiskit/synthesis/arithmetic/__init__.py +1 -1
  118. qiskit/synthesis/arithmetic/adders/__init__.py +1 -0
  119. qiskit/synthesis/arithmetic/adders/draper_qft_adder.py +6 -2
  120. qiskit/synthesis/arithmetic/adders/rv_ripple_carry_adder.py +156 -0
  121. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +14 -126
  122. qiskit/synthesis/discrete_basis/solovay_kitaev.py +161 -121
  123. qiskit/synthesis/evolution/lie_trotter.py +10 -7
  124. qiskit/synthesis/evolution/product_formula.py +10 -7
  125. qiskit/synthesis/evolution/qdrift.py +10 -7
  126. qiskit/synthesis/evolution/suzuki_trotter.py +10 -7
  127. qiskit/synthesis/multi_controlled/__init__.py +4 -0
  128. qiskit/synthesis/multi_controlled/mcx_synthesis.py +402 -178
  129. qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py +14 -15
  130. qiskit/synthesis/qft/qft_decompose_lnn.py +7 -25
  131. qiskit/synthesis/unitary/qsd.py +80 -9
  132. qiskit/transpiler/__init__.py +19 -8
  133. qiskit/transpiler/instruction_durations.py +2 -20
  134. qiskit/transpiler/passes/__init__.py +4 -2
  135. qiskit/transpiler/passes/layout/dense_layout.py +26 -6
  136. qiskit/transpiler/passes/layout/disjoint_utils.py +1 -166
  137. qiskit/transpiler/passes/layout/sabre_layout.py +22 -3
  138. qiskit/transpiler/passes/layout/sabre_pre_layout.py +1 -1
  139. qiskit/transpiler/passes/layout/vf2_layout.py +49 -13
  140. qiskit/transpiler/passes/layout/vf2_utils.py +13 -1
  141. qiskit/transpiler/passes/optimization/__init__.py +1 -1
  142. qiskit/transpiler/passes/optimization/consolidate_blocks.py +6 -1
  143. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +2 -1
  144. qiskit/transpiler/passes/optimization/optimize_clifford_t.py +68 -0
  145. qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +3 -9
  146. qiskit/transpiler/passes/routing/sabre_swap.py +12 -2
  147. qiskit/transpiler/passes/routing/star_prerouting.py +106 -81
  148. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  149. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
  150. qiskit/transpiler/passes/scheduling/padding/__init__.py +1 -0
  151. qiskit/transpiler/passes/scheduling/padding/context_aware_dynamical_decoupling.py +876 -0
  152. qiskit/transpiler/passes/synthesis/__init__.py +1 -0
  153. qiskit/transpiler/passes/synthesis/clifford_unitary_synth_plugin.py +123 -0
  154. qiskit/transpiler/passes/synthesis/hls_plugins.py +472 -92
  155. qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +27 -22
  156. qiskit/transpiler/passmanager_config.py +3 -0
  157. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +149 -28
  158. qiskit/transpiler/preset_passmanagers/common.py +101 -0
  159. qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +6 -0
  160. qiskit/transpiler/preset_passmanagers/level3.py +2 -2
  161. qiskit/utils/optionals.py +6 -5
  162. qiskit/visualization/circuit/_utils.py +5 -3
  163. qiskit/visualization/circuit/latex.py +9 -2
  164. qiskit/visualization/circuit/matplotlib.py +26 -4
  165. qiskit/visualization/circuit/qcstyle.py +9 -157
  166. qiskit/visualization/dag/__init__.py +13 -0
  167. qiskit/visualization/dag/dagstyle.py +103 -0
  168. qiskit/visualization/dag/styles/__init__.py +13 -0
  169. qiskit/visualization/dag/styles/color.json +10 -0
  170. qiskit/visualization/dag/styles/plain.json +5 -0
  171. qiskit/visualization/dag_visualization.py +169 -98
  172. qiskit/visualization/style.py +223 -0
  173. {qiskit-2.0.1.dist-info → qiskit-2.1.0rc1.dist-info}/METADATA +14 -13
  174. {qiskit-2.0.1.dist-info → qiskit-2.1.0rc1.dist-info}/RECORD +178 -169
  175. {qiskit-2.0.1.dist-info → qiskit-2.1.0rc1.dist-info}/WHEEL +1 -1
  176. {qiskit-2.0.1.dist-info → qiskit-2.1.0rc1.dist-info}/entry_points.txt +6 -0
  177. qiskit/synthesis/discrete_basis/commutator_decompose.py +0 -265
  178. qiskit/synthesis/discrete_basis/gate_sequence.py +0 -421
  179. {qiskit-2.0.1.dist-info → qiskit-2.1.0rc1.dist-info}/licenses/LICENSE.txt +0 -0
  180. {qiskit-2.0.1.dist-info → qiskit-2.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -194,6 +194,26 @@ not sufficient, the corresponding synthesis method will return `None`.
194
194
  - `0`
195
195
  - `k-2`
196
196
  - at most `8*k-6` CX gates
197
+ * - ``"2_clean_kg24"``
198
+ - :class:`~.MCXSynthesis2CleanKG24`
199
+ - `2`
200
+ - `0`
201
+ - at most `12*k-18` CX gates
202
+ * - ``"2_dirty_kg24"``
203
+ - :class:`~.MCXSynthesis2DirtyKG24`
204
+ - `0`
205
+ - `2`
206
+ - at most `24*k-48` CX gates
207
+ * - ``"1_clean_kg24"``
208
+ - :class:`~.MCXSynthesis1CleanKG24`
209
+ - `1`
210
+ - `0`
211
+ - at most `12*k-18` CX gates
212
+ * - ``"1_dirty_kg24"``
213
+ - :class:`~.MCXSynthesis1DirtyKG24`
214
+ - `0`
215
+ - `1`
216
+ - at most `24*k-48` CX gates
197
217
  * - ``"1_clean_b95"``
198
218
  - :class:`~.MCXSynthesis1CleanB95`
199
219
  - `1`
@@ -212,6 +232,10 @@ not sufficient, the corresponding synthesis method will return `None`.
212
232
  MCXSynthesisNoAuxV24
213
233
  MCXSynthesisNCleanM15
214
234
  MCXSynthesisNDirtyI15
235
+ MCXSynthesis2CleanKG24
236
+ MCXSynthesis2DirtyKG24
237
+ MCXSynthesis1CleanKG24
238
+ MCXSynthesis1DirtyKG24
215
239
  MCXSynthesis1CleanB95
216
240
  MCXSynthesisDefault
217
241
 
@@ -379,6 +403,10 @@ Half Adder Synthesis
379
403
  - :class:`.HalfAdderSynthesisC04`
380
404
  - 1
381
405
  - a ripple-carry adder
406
+ * - ``"ripple_r25"``
407
+ - :class:`.HalfAdderSynthesisR25`
408
+ - 0
409
+ - a ripple-carry adder with no ancillas
382
410
  * - ``"ripple_vbe"``
383
411
  - :class:`.HalfAdderSynthesisV95`
384
412
  - :math:`n-1`, for :math:`n`-bit numbers
@@ -398,6 +426,7 @@ Half Adder Synthesis
398
426
  HalfAdderSynthesisC04
399
427
  HalfAdderSynthesisD00
400
428
  HalfAdderSynthesisV95
429
+ HalfAdderSynthesisR25
401
430
  HalfAdderSynthesisDefault
402
431
 
403
432
  Full Adder Synthesis
@@ -488,6 +517,7 @@ from qiskit.circuit.annotated_operation import (
488
517
  ControlModifier,
489
518
  InverseModifier,
490
519
  PowerModifier,
520
+ _canonicalize_modifiers,
491
521
  )
492
522
  from qiskit.transpiler.coupling import CouplingMap
493
523
 
@@ -521,7 +551,11 @@ from qiskit.synthesis.qft import (
521
551
  )
522
552
  from qiskit.synthesis.multi_controlled import (
523
553
  synth_mcx_n_dirty_i15,
554
+ synth_mcx_2_dirty_kg24,
555
+ synth_mcx_1_dirty_kg24,
524
556
  synth_mcx_n_clean_m15,
557
+ synth_mcx_2_clean_kg24,
558
+ synth_mcx_1_clean_kg24,
525
559
  synth_mcx_1_clean_b95,
526
560
  synth_mcx_gray_code,
527
561
  synth_mcx_noaux_v24,
@@ -532,14 +566,16 @@ from qiskit.synthesis.arithmetic import (
532
566
  adder_ripple_c04,
533
567
  adder_qft_d00,
534
568
  adder_ripple_v95,
569
+ adder_ripple_r25,
535
570
  multiplier_qft_r17,
536
571
  multiplier_cumulative_h18,
537
572
  )
538
573
  from qiskit.quantum_info.operators import Clifford
539
574
  from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper
540
575
  from qiskit.transpiler.exceptions import TranspilerError
576
+ from qiskit.circuit._add_control import EFFICIENTLY_CONTROLLED_GATES
541
577
 
542
- from qiskit._accelerate.high_level_synthesis import synthesize_operation
578
+ from qiskit._accelerate.high_level_synthesis import synthesize_operation, HighLevelSynthesisData
543
579
  from .plugin import HighLevelSynthesisPlugin
544
580
 
545
581
 
@@ -1008,9 +1044,10 @@ class MCXSynthesisNDirtyI15(HighLevelSynthesisPlugin):
1008
1044
  This plugin name is :``mcx.n_dirty_i15`` which can be used as the key on
1009
1045
  an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
1010
1046
 
1011
- For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis
1047
+ For a multi-controlled X gate with :math:`k\ge 4` control qubits this synthesis
1012
1048
  method requires :math:`k - 2` additional dirty auxiliary qubits. The synthesized
1013
1049
  circuit consists of :math:`2 * k - 1` qubits and at most :math:`8 * k - 6` CX gates.
1050
+ For :math:`k\le 3` explicit efficient circuits are used instead.
1014
1051
 
1015
1052
  The plugin supports the following plugin-specific options:
1016
1053
 
@@ -1105,14 +1142,14 @@ class MCXSynthesis1CleanB95(HighLevelSynthesisPlugin):
1105
1142
 
1106
1143
  For a multi-controlled X gate with :math:`k\ge 5` control qubits this synthesis
1107
1144
  method requires a single additional clean auxiliary qubit. The synthesized
1108
- circuit consists of :math:`k + 2` qubits and at most :math:`16 * k - 8` CX gates.
1145
+ circuit consists of :math:`k + 2` qubits and at most :math:`16 * k - 24` CX gates.
1109
1146
 
1110
1147
  The plugin supports the following plugin-specific options:
1111
1148
 
1112
1149
  * num_clean_ancillas: The number of clean auxiliary qubits available.
1113
1150
 
1114
1151
  References:
1115
- 1. Barenco et. al., Phys.Rev. A52 3457 (1995),
1152
+ 1. Barenco et. al., *Elementary gates for quantum computation*, Phys.Rev. A52 3457 (1995),
1116
1153
  `arXiv:quant-ph/9503016 <https://arxiv.org/abs/quant-ph/9503016>`_
1117
1154
  """
1118
1155
 
@@ -1142,6 +1179,174 @@ class MCXSynthesis1CleanB95(HighLevelSynthesisPlugin):
1142
1179
  return decomposition
1143
1180
 
1144
1181
 
1182
+ class MCXSynthesis2CleanKG24(HighLevelSynthesisPlugin):
1183
+ r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
1184
+ Gidney (2024).
1185
+
1186
+ See [1] for details.
1187
+
1188
+ The plugin name is :``mcx.2_clean_kg24`` which can be used as the key on an :class:`~.HLSConfig`
1189
+ object to use this method with :class:`~.HighLevelSynthesis`.
1190
+
1191
+ For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
1192
+ :math:`2` additional clean ancillary qubits. The synthesized circuit consists of :math:`k + 2`
1193
+ qubits and at most :math:`12 * k - 18` CX gates.
1194
+
1195
+ The plugin supports the following plugin-specific options:
1196
+
1197
+ * num_clean_ancillas: The number of clean ancillary qubits available.
1198
+
1199
+ References:
1200
+ 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
1201
+ `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
1202
+ """
1203
+
1204
+ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
1205
+ """Run synthesis for the given MCX gate."""
1206
+
1207
+ if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
1208
+ # Unfortunately we occasionally have custom instructions called "mcx"
1209
+ # which get wrongly caught by the plugin interface. A simple solution is
1210
+ # to return None in this case, since HLS would proceed to examine
1211
+ # their definition as it should.
1212
+ return None
1213
+
1214
+ num_ctrl_qubits = high_level_object.num_ctrl_qubits
1215
+ num_clean_ancillas = options.get("num_clean_ancillas", 0)
1216
+
1217
+ if num_clean_ancillas < 2:
1218
+ return None
1219
+
1220
+ decomposition = synth_mcx_2_clean_kg24(num_ctrl_qubits)
1221
+ return decomposition
1222
+
1223
+
1224
+ class MCXSynthesis2DirtyKG24(HighLevelSynthesisPlugin):
1225
+ r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
1226
+ Gidney (2024).
1227
+
1228
+ See [1] for details.
1229
+
1230
+ The plugin name is :``mcx.2_dirty_kg24`` which can be used as the key on an :class:`~.HLSConfig`
1231
+ object to use this method with :class:`~.HighLevelSynthesis`.
1232
+
1233
+ For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
1234
+ :math:`2` additional dirty ancillary qubits. The synthesized circuit consists of :math:`k + 2`
1235
+ qubits and at most :math:`24 * k - 48` CX gates.
1236
+
1237
+ The plugin supports the following plugin-specific options:
1238
+
1239
+ * num_clean_ancillas: The number of clean ancillary qubits available.
1240
+
1241
+ References:
1242
+ 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
1243
+ `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
1244
+ """
1245
+
1246
+ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
1247
+ """Run synthesis for the given MCX gate."""
1248
+
1249
+ if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
1250
+ # Unfortunately we occasionally have custom instructions called "mcx"
1251
+ # which get wrongly caught by the plugin interface. A simple solution is
1252
+ # to return None in this case, since HLS would proceed to examine
1253
+ # their definition as it should.
1254
+ return None
1255
+
1256
+ num_ctrl_qubits = high_level_object.num_ctrl_qubits
1257
+ num_dirty_ancillas = options.get("num_dirty_ancillas", 0)
1258
+
1259
+ if num_dirty_ancillas < 2:
1260
+ return None
1261
+
1262
+ decomposition = synth_mcx_2_dirty_kg24(num_ctrl_qubits)
1263
+ return decomposition
1264
+
1265
+
1266
+ class MCXSynthesis1CleanKG24(HighLevelSynthesisPlugin):
1267
+ r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
1268
+ Gidney (2024).
1269
+
1270
+ See [1] for details.
1271
+
1272
+ The plugin name is :``mcx.1_clean_kg24`` which can be used as the key on an :class:`~.HLSConfig`
1273
+ object to use this method with :class:`~.HighLevelSynthesis`.
1274
+
1275
+ For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
1276
+ :math:`1` additional clean ancillary qubit. The synthesized circuit consists of :math:`k + 2`
1277
+ qubits and at most :math:`12 * k - 18` CX gates.
1278
+
1279
+ The plugin supports the following plugin-specific options:
1280
+
1281
+ * num_clean_ancillas: The number of clean ancillary qubits available.
1282
+
1283
+ References:
1284
+ 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
1285
+ `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
1286
+ """
1287
+
1288
+ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
1289
+ """Run synthesis for the given MCX gate."""
1290
+
1291
+ if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
1292
+ # Unfortunately we occasionally have custom instructions called "mcx"
1293
+ # which get wrongly caught by the plugin interface. A simple solution is
1294
+ # to return None in this case, since HLS would proceed to examine
1295
+ # their definition as it should.
1296
+ return None
1297
+
1298
+ num_ctrl_qubits = high_level_object.num_ctrl_qubits
1299
+ num_clean_ancillas = options.get("num_clean_ancillas", 0)
1300
+
1301
+ if num_clean_ancillas < 1:
1302
+ return None
1303
+
1304
+ decomposition = synth_mcx_1_clean_kg24(num_ctrl_qubits)
1305
+ return decomposition
1306
+
1307
+
1308
+ class MCXSynthesis1DirtyKG24(HighLevelSynthesisPlugin):
1309
+ r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
1310
+ Gidney (2024).
1311
+
1312
+ See [1] for details.
1313
+
1314
+ The plugin name is :``mcx.1_dirty_kg24`` which can be used as the key on an :class:`~.HLSConfig`
1315
+ object to use this method with :class:`~.HighLevelSynthesis`.
1316
+
1317
+ For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
1318
+ :math:`1` additional dirty ancillary qubit. The synthesized circuit consists of :math:`k + 2`
1319
+ qubits and at most :math:`24 * k - 48` CX gates.
1320
+
1321
+ The plugin supports the following plugin-specific options:
1322
+
1323
+ * num_clean_ancillas: The number of clean ancillary qubits available.
1324
+
1325
+ References:
1326
+ 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
1327
+ `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
1328
+ """
1329
+
1330
+ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
1331
+ """Run synthesis for the given MCX gate."""
1332
+
1333
+ if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
1334
+ # Unfortunately we occasionally have custom instructions called "mcx"
1335
+ # which get wrongly caught by the plugin interface. A simple solution is
1336
+ # to return None in this case, since HLS would proceed to examine
1337
+ # their definition as it should.
1338
+ return None
1339
+
1340
+ num_ctrl_qubits = high_level_object.num_ctrl_qubits
1341
+ num_dirty_ancillas = options.get("num_dirty_ancillas", 0)
1342
+
1343
+ if num_dirty_ancillas < 1:
1344
+ return None
1345
+
1346
+ decomposition = synth_mcx_1_dirty_kg24(num_ctrl_qubits)
1347
+ return decomposition
1348
+
1349
+
1145
1350
  class MCXSynthesisGrayCode(HighLevelSynthesisPlugin):
1146
1351
  r"""Synthesis plugin for a multi-controlled X gate based on the Gray code.
1147
1352
 
@@ -1225,27 +1430,23 @@ class MCXSynthesisDefault(HighLevelSynthesisPlugin):
1225
1430
 
1226
1431
  # Iteratively run other synthesis methods available
1227
1432
 
1228
- if (
1229
- decomposition := MCXSynthesisNCleanM15().run(
1230
- high_level_object, coupling_map, target, qubits, **options
1231
- )
1232
- ) is not None:
1233
- return decomposition
1234
-
1235
- if (
1236
- decomposition := MCXSynthesisNDirtyI15().run(
1237
- high_level_object, coupling_map, target, qubits, **options
1238
- )
1239
- ) is not None:
1240
- return decomposition
1241
-
1242
- if (
1243
- decomposition := MCXSynthesis1CleanB95().run(
1244
- high_level_object, coupling_map, target, qubits, **options
1245
- )
1246
- ) is not None:
1247
- return decomposition
1433
+ for synthesis_method in [
1434
+ MCXSynthesis2CleanKG24,
1435
+ MCXSynthesis1CleanKG24,
1436
+ MCXSynthesisNCleanM15,
1437
+ MCXSynthesisNDirtyI15,
1438
+ MCXSynthesis2DirtyKG24,
1439
+ MCXSynthesis1DirtyKG24,
1440
+ MCXSynthesis1CleanB95,
1441
+ ]:
1442
+ if (
1443
+ decomposition := synthesis_method().run(
1444
+ high_level_object, coupling_map, target, qubits, **options
1445
+ )
1446
+ ) is not None:
1447
+ return decomposition
1248
1448
 
1449
+ # If no synthesis method was successful, fall back to the default
1249
1450
  return MCXSynthesisNoAuxV24().run(
1250
1451
  high_level_object, coupling_map, target, qubits, **options
1251
1452
  )
@@ -1463,7 +1664,7 @@ class ModularAdderSynthesisD00(HighLevelSynthesisPlugin):
1463
1664
  if not isinstance(high_level_object, ModularAdderGate):
1464
1665
  return None
1465
1666
 
1466
- return adder_qft_d00(high_level_object.num_state_qubits, kind="fixed")
1667
+ return adder_qft_d00(high_level_object.num_state_qubits, kind="fixed", annotated=True)
1467
1668
 
1468
1669
 
1469
1670
  class HalfAdderSynthesisDefault(HighLevelSynthesisPlugin):
@@ -1490,13 +1691,17 @@ class HalfAdderSynthesisDefault(HighLevelSynthesisPlugin):
1490
1691
  if not isinstance(high_level_object, HalfAdderGate):
1491
1692
  return None
1492
1693
 
1493
- # For up to 3 qubits, ripple_v95 is better (if there are enough ancilla qubits)
1494
- if high_level_object.num_state_qubits <= 3:
1495
- decomposition = HalfAdderSynthesisV95().run(
1496
- high_level_object, coupling_map, target, qubits, **options
1694
+ # For up to 3 qubits, ripple_r25 is better
1695
+ if (
1696
+ high_level_object.num_state_qubits <= 3
1697
+ and (
1698
+ decomposition := HalfAdderSynthesisR25().run(
1699
+ high_level_object, coupling_map, target, qubits, **options
1700
+ )
1497
1701
  )
1498
- if decomposition is not None:
1499
- return decomposition
1702
+ is not None
1703
+ ):
1704
+ return decomposition
1500
1705
 
1501
1706
  # The next best option is to use ripple_c04 (if there are enough ancilla qubits)
1502
1707
  if (
@@ -1506,8 +1711,8 @@ class HalfAdderSynthesisDefault(HighLevelSynthesisPlugin):
1506
1711
  ) is not None:
1507
1712
  return decomposition
1508
1713
 
1509
- # The QFT-based adder does not require ancilla qubits and should always succeed
1510
- return HalfAdderSynthesisD00().run(
1714
+ # The ripple_rv_25 adder does not require ancilla qubits and should always succeed
1715
+ return HalfAdderSynthesisR25().run(
1511
1716
  high_level_object, coupling_map, target, qubits, **options
1512
1717
  )
1513
1718
 
@@ -1564,6 +1769,22 @@ class HalfAdderSynthesisV95(HighLevelSynthesisPlugin):
1564
1769
  return adder_ripple_v95(num_state_qubits, kind="half")
1565
1770
 
1566
1771
 
1772
+ class HalfAdderSynthesisR25(HighLevelSynthesisPlugin):
1773
+ """A ripple-carry adder with a carry-out bit with no ancillary qubits.
1774
+
1775
+ This plugin name is:``HalfAdder.ripple_r25`` which can be used as the key on an
1776
+ :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
1777
+
1778
+ """
1779
+
1780
+ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
1781
+ if not isinstance(high_level_object, HalfAdderGate):
1782
+ return None
1783
+
1784
+ num_state_qubits = high_level_object.num_state_qubits
1785
+ return adder_ripple_r25(num_state_qubits)
1786
+
1787
+
1567
1788
  class HalfAdderSynthesisD00(HighLevelSynthesisPlugin):
1568
1789
  """A QFT-based adder with a carry-in and a carry-out bit.
1569
1790
 
@@ -1575,7 +1796,7 @@ class HalfAdderSynthesisD00(HighLevelSynthesisPlugin):
1575
1796
  if not isinstance(high_level_object, HalfAdderGate):
1576
1797
  return None
1577
1798
 
1578
- return adder_qft_d00(high_level_object.num_state_qubits, kind="half")
1799
+ return adder_qft_d00(high_level_object.num_state_qubits, kind="half", annotated=True)
1579
1800
 
1580
1801
 
1581
1802
  class FullAdderSynthesisDefault(HighLevelSynthesisPlugin):
@@ -1696,13 +1917,15 @@ class PauliEvolutionSynthesisDefault(HighLevelSynthesisPlugin):
1696
1917
  # Don't do anything if a gate is called "evolution" but is not an
1697
1918
  # actual PauliEvolutionGate
1698
1919
  return None
1699
-
1700
1920
  algo = high_level_object.synthesis
1701
1921
 
1922
+ original_preserve_order = algo.preserve_order
1702
1923
  if "preserve_order" in options and isinstance(algo, ProductFormula):
1703
1924
  algo.preserve_order = options["preserve_order"]
1704
1925
 
1705
- return algo.synthesize(high_level_object)
1926
+ synth_object = algo.synthesize(high_level_object)
1927
+ algo.preserve_order = original_preserve_order
1928
+ return synth_object
1706
1929
 
1707
1930
 
1708
1931
  class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
@@ -1753,6 +1976,7 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
1753
1976
  )
1754
1977
  return None
1755
1978
 
1979
+ original_preserve_order = algo.preserve_order
1756
1980
  if "preserve_order" in options:
1757
1981
  algo.preserve_order = options["preserve_order"]
1758
1982
 
@@ -1765,7 +1989,7 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
1765
1989
  upto_phase = options.get("upto_phase", False)
1766
1990
  resynth_clifford_method = options.get("resynth_clifford_method", 1)
1767
1991
 
1768
- return synth_pauli_network_rustiq(
1992
+ synth_object = synth_pauli_network_rustiq(
1769
1993
  num_qubits=num_qubits,
1770
1994
  pauli_network=pauli_network,
1771
1995
  optimize_count=optimize_count,
@@ -1774,6 +1998,8 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
1774
1998
  upto_phase=upto_phase,
1775
1999
  resynth_clifford_method=resynth_clifford_method,
1776
2000
  )
2001
+ algo.preserve_order = original_preserve_order
2002
+ return synth_object
1777
2003
 
1778
2004
 
1779
2005
  class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
@@ -1784,9 +2010,17 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
1784
2010
  """
1785
2011
 
1786
2012
  def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
2013
+ # The plugin is triggered based on the name (i.e. for operations called "annotated").
2014
+ # However, we should only do something when the operation is truthfully an AnnotatedOperation.
1787
2015
  if not isinstance(high_level_object, AnnotatedOperation):
1788
2016
  return None
1789
2017
 
2018
+ # Combine the modifiers. If there were no modifiers, or the modifiers magically canceled out,
2019
+ # return the quantum circuit containing the base operation.
2020
+ high_level_object = self._canonicalize_op(high_level_object)
2021
+ if not isinstance(high_level_object, AnnotatedOperation):
2022
+ return self._instruction_to_circuit(high_level_object)
2023
+
1790
2024
  operation = high_level_object
1791
2025
  modifiers = high_level_object.modifiers
1792
2026
 
@@ -1802,67 +2036,110 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
1802
2036
  "The AnnotatedSynthesisDefault plugin should receive data and input_qubits via options."
1803
2037
  )
1804
2038
 
1805
- if len(modifiers) > 0:
1806
- num_ctrl = sum(
1807
- mod.num_ctrl_qubits for mod in modifiers if isinstance(mod, ControlModifier)
1808
- )
1809
- power = sum(mod.power for mod in modifiers if isinstance(mod, PowerModifier))
1810
- is_inverted = sum(1 for mod in modifiers if isinstance(mod, InverseModifier)) % 2
1811
-
1812
- # First, synthesize the base operation of this annotated operation.
1813
- # As this step cannot use any control qubits as auxiliary qubits, we use a dedicated
1814
- # tracker (annotated_tracker).
1815
- # The logic is as follows:
1816
- # - annotated_tracker.disable control qubits
1817
- # - if have power or inverse modifiers, annotated_tracker.set_dirty(base_qubits)
1818
- # - synthesize the base operation using annotated tracker
1819
- # - main_tracker.set_dirty(base_qubits)
1820
- #
1821
- # Note that we need to set the base_qubits to dirty if we have power or inverse
1822
- # modifiers. For power: even if the power is a positive integer (that is, we need
1823
- # to repeat the same circuit multiple times), even if the target is initially at |0>,
1824
- # it will generally not be at |0> after one iteration. For inverse: as we
1825
- # flip the order of operations, we cannot exploit which qubits are at |0> as "viewed from
1826
- # the back of the circuit". If we just have control modifiers, we can use the state
1827
- # of base qubits when synthesizing the controlled operation.
1828
- #
1829
- # In addition, all of the other global qubits that are not a part of the annotated
1830
- # operation can be used as they are in all cases, since we are assuming that all of
1831
- # the synthesis methods preserve the states of ancilla qubits.
1832
- annotated_tracker = tracker.copy()
1833
- control_qubits = input_qubits[:num_ctrl]
1834
- base_qubits = input_qubits[num_ctrl:]
1835
- annotated_tracker.disable(control_qubits) # do not access control qubits
1836
- if power != 0 or is_inverted:
1837
- annotated_tracker.set_dirty(base_qubits)
1838
-
1839
- # Note that synthesize_operation also returns the output qubits on which the
1840
- # operation is defined, however currently the plugin mechanism has no way
1841
- # to return these (and instead the upstream code greedily grabs some ancilla
1842
- # qubits from the circuit). We should refactor the plugin "run" iterface to
1843
- # return the actual ancilla qubits used.
1844
- synthesized_base_op_result = synthesize_operation(
1845
- operation.base_op, input_qubits[num_ctrl:], data, annotated_tracker
1846
- )
2039
+ # The synthesis consists of two steps:
2040
+ # - First, we synthesize the base operation.
2041
+ # - Second, we apply modifiers to this circuit.
2042
+ #
2043
+ # An important optimization (similar to the code in ``add_control.py``) is to synthesize
2044
+ # the base operation with respect to a larger set of "basis" gates, to which the control
2045
+ # logic can be added more efficiently. In addition, we add annotated operations to be
2046
+ # in this larger set, exploiting the fact that adding control to annotated operations
2047
+ # returns a new annotated operation with an extended list of modifiers.
2048
+ #
2049
+ # Note that it is fine for this function to return a circuit with high-level objects
2050
+ # (including annotated operations) as the HighLevelSynthesis transpiler pass will
2051
+ # recursively re-synthesize this circuit, However, we should always guarantee that some
2052
+ # progress is made.
2053
+ basis = set(EFFICIENTLY_CONTROLLED_GATES + ["annotated", "mcx", "qft"])
2054
+
2055
+ base_synthesis_data = HighLevelSynthesisData(
2056
+ hls_config=data.hls_config,
2057
+ hls_plugin_manager=data.hls_plugin_manager,
2058
+ coupling_map=None,
2059
+ target=None,
2060
+ equivalence_library=data.equivalence_library,
2061
+ hls_op_names=data.hls_op_names,
2062
+ device_insts=basis,
2063
+ use_physical_indices=data.use_physical_indices,
2064
+ min_qubits=0,
2065
+ unroll_definitions=data.unroll_definitions,
2066
+ )
1847
2067
 
1848
- # The base operation does not need to be synthesized.
1849
- # For simplicity, we wrap the instruction into a circuit. Note that
1850
- # this should not deteriorate the quality of the result.
1851
- if synthesized_base_op_result is None:
1852
- synthesized_base_op = self._instruction_to_circuit(operation.base_op)
1853
- else:
1854
- synthesized_base_op = QuantumCircuit._from_circuit_data(
1855
- synthesized_base_op_result[0]
1856
- )
1857
- tracker.set_dirty(base_qubits)
2068
+ num_ctrl = sum(mod.num_ctrl_qubits for mod in modifiers if isinstance(mod, ControlModifier))
2069
+ power = sum(mod.power for mod in modifiers if isinstance(mod, PowerModifier))
2070
+ is_inverted = sum(1 for mod in modifiers if isinstance(mod, InverseModifier)) % 2
2071
+
2072
+ # First, synthesize the base operation of this annotated operation.
2073
+ # As this step cannot use any control qubits as auxiliary qubits, we use a dedicated
2074
+ # tracker (annotated_tracker).
2075
+ # The logic is as follows:
2076
+ # - annotated_tracker.disable control qubits
2077
+ # - if have power or inverse modifiers, annotated_tracker.set_dirty(base_qubits)
2078
+ # - synthesize the base operation using annotated tracker
2079
+ # - main_tracker.set_dirty(base_qubits)
2080
+ #
2081
+ # Note that we need to set the base_qubits to dirty if we have power or inverse
2082
+ # modifiers. For power: even if the power is a positive integer (that is, we need
2083
+ # to repeat the same circuit multiple times), even if the target is initially at |0>,
2084
+ # it will generally not be at |0> after one iteration. For inverse: as we
2085
+ # flip the order of operations, we cannot exploit which qubits are at |0> as "viewed from
2086
+ # the back of the circuit". If we just have control modifiers, we can use the state
2087
+ # of base qubits when synthesizing the controlled operation.
2088
+ #
2089
+ # In addition, all of the other global qubits that are not a part of the annotated
2090
+ # operation can be used as they are in all cases, since we are assuming that all of
2091
+ # the synthesis methods preserve the states of ancilla qubits.
2092
+ annotated_tracker = tracker.copy()
2093
+ control_qubits = input_qubits[:num_ctrl]
2094
+ base_qubits = input_qubits[num_ctrl:]
2095
+ annotated_tracker.disable(control_qubits) # do not access control qubits
2096
+ if power != 0 or is_inverted:
2097
+ annotated_tracker.set_dirty(base_qubits)
2098
+
2099
+ # Note that synthesize_operation also returns the output qubits on which the
2100
+ # operation is defined, however currently the plugin mechanism has no way
2101
+ # to return these (and instead the upstream code greedily grabs some ancilla
2102
+ # qubits from the circuit). We should refactor the plugin "run" iterface to
2103
+ # return the actual ancilla qubits used.
2104
+ synthesized_base_op_result = synthesize_operation(
2105
+ operation.base_op, base_qubits, base_synthesis_data, annotated_tracker
2106
+ )
1858
2107
 
2108
+ # The base operation does not need to be synthesized.
2109
+ # For simplicity, we wrap the instruction into a circuit. Note that
2110
+ # this should not deteriorate the quality of the result.
2111
+ if synthesized_base_op_result is None:
2112
+ synthesized_base_op = self._instruction_to_circuit(operation.base_op)
2113
+ else:
2114
+ synthesized_base_op = QuantumCircuit._from_circuit_data(synthesized_base_op_result[0])
2115
+ tracker.set_dirty(base_qubits)
2116
+
2117
+ # As one simple optimization, we apply conjugate decomposition to the circuit obtained
2118
+ # while synthesizing the base operator.
2119
+ conjugate_decomp = self._conjugate_decomposition(synthesized_base_op)
2120
+
2121
+ if conjugate_decomp is None:
2122
+ # Apply annotations to the whole circuit.
1859
2123
  # This step currently does not introduce ancilla qubits. However it makes
1860
2124
  # a lot of sense to allow this in the future.
1861
2125
  synthesized = self._apply_annotations(synthesized_base_op, operation.modifiers)
2126
+ else:
2127
+ # Apply annotations only to the middle part of the circuit.
2128
+ (front, middle, back) = conjugate_decomp
2129
+ synthesized = QuantumCircuit(operation.num_qubits)
2130
+ synthesized.compose(
2131
+ front, synthesized.qubits[num_ctrl : operation.num_qubits], inplace=True
2132
+ )
2133
+ synthesized.compose(
2134
+ self._apply_annotations(middle, operation.modifiers),
2135
+ synthesized.qubits,
2136
+ inplace=True,
2137
+ )
2138
+ synthesized.compose(
2139
+ back, synthesized.qubits[num_ctrl : operation.num_qubits], inplace=True
2140
+ )
1862
2141
 
1863
- return synthesized
1864
-
1865
- return None
2142
+ return synthesized
1866
2143
 
1867
2144
  @staticmethod
1868
2145
  def _apply_annotations(circuit: QuantumCircuit, modifiers: list[Modifier]) -> QuantumCircuit:
@@ -1926,6 +2203,109 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
1926
2203
  circuit.append(op, circuit.qubits, circuit.clbits)
1927
2204
  return circuit
1928
2205
 
2206
+ @staticmethod
2207
+ def _instruction_to_circuit(op: Operation) -> QuantumCircuit:
2208
+ """Wraps a single operation into a quantum circuit."""
2209
+ circuit = QuantumCircuit(op.num_qubits, op.num_clbits)
2210
+ circuit.append(op, circuit.qubits, circuit.clbits)
2211
+ return circuit
2212
+
2213
+ @staticmethod
2214
+ def _canonicalize_op(op: Operation) -> Operation:
2215
+ """
2216
+ Combines recursive annotated operations and canonicalizes modifiers.
2217
+ """
2218
+ cur = op
2219
+ all_modifiers = []
2220
+
2221
+ while isinstance(cur, AnnotatedOperation):
2222
+ all_modifiers.append(cur.modifiers)
2223
+ cur = cur.base_op
2224
+
2225
+ new_modifiers = []
2226
+ for modifiers in all_modifiers[::-1]:
2227
+ new_modifiers.extend(modifiers)
2228
+
2229
+ canonical_modifiers = _canonicalize_modifiers(new_modifiers)
2230
+
2231
+ if not canonical_modifiers:
2232
+ return cur
2233
+
2234
+ return AnnotatedOperation(cur, canonical_modifiers)
2235
+
2236
+ @staticmethod
2237
+ def _are_inverse_ops(inst1: "CircuitInstruction", inst2: "CircuitInstruction"):
2238
+ """A very naive function that checks whether two circuit instructions are inverse of
2239
+ each other. The main use-case covered is a ``QFTGate`` and its inverse, represented as
2240
+ an ``AnnotatedOperation`` with a single ``InverseModifier``.
2241
+ """
2242
+ res = False
2243
+
2244
+ if (
2245
+ inst1.qubits != inst2.qubits
2246
+ or inst1.clbits != inst2.clbits
2247
+ or len(inst1.params) != len(inst2.params)
2248
+ ):
2249
+ return False
2250
+
2251
+ op1 = inst1.operation
2252
+ op2 = inst2.operation
2253
+
2254
+ ann1 = isinstance(op1, AnnotatedOperation)
2255
+ ann2 = isinstance(op2, AnnotatedOperation)
2256
+
2257
+ if not ann1 and not ann2:
2258
+ res = op1 == op2.inverse()
2259
+ elif not ann1 and ann2 and op2.modifiers == [InverseModifier()]:
2260
+ res = op1 == op2.base_op
2261
+ elif not ann2 and ann1 and op1.modifiers == [InverseModifier()]:
2262
+ res = op1.base_op == op2
2263
+
2264
+ return res
2265
+
2266
+ @staticmethod
2267
+ def _conjugate_decomposition(
2268
+ circuit: QuantumCircuit,
2269
+ ) -> tuple[QuantumCircuit, QuantumCircuit, QuantumCircuit] | None:
2270
+ """
2271
+ Decomposes a circuit ``A`` into 3 sub-circuits ``P``, ``Q``, ``R`` such that
2272
+ ``A = P -- Q -- R`` and ``R = P^{-1}``.
2273
+
2274
+ This is accomplished by iteratively finding inverse nodes at the front and at the back of the
2275
+ circuit.
2276
+
2277
+ The function returns ``None`` when ``P`` and ``R`` are empty.
2278
+ """
2279
+ num_gates = circuit.size()
2280
+
2281
+ idx = 0
2282
+ ridx = num_gates - 1
2283
+
2284
+ while True:
2285
+ if idx >= ridx:
2286
+ break
2287
+ if AnnotatedSynthesisDefault._are_inverse_ops(circuit[idx], circuit[ridx]):
2288
+ idx += 1
2289
+ ridx -= 1
2290
+ else:
2291
+ break
2292
+
2293
+ if idx == 0:
2294
+ return None
2295
+
2296
+ front_circuit = circuit.copy_empty_like()
2297
+ front_circuit.global_phase = 0
2298
+ for i in range(0, idx):
2299
+ front_circuit.append(circuit[i])
2300
+ middle_circuit = circuit.copy_empty_like() # inherits the global phase
2301
+ for i in range(idx, ridx + 1):
2302
+ middle_circuit.append(circuit[i])
2303
+ back_circuit = circuit.copy_empty_like()
2304
+ back_circuit.global_phase = 0
2305
+ for i in range(ridx + 1, num_gates):
2306
+ back_circuit.append(circuit[i])
2307
+ return (front_circuit, middle_circuit, back_circuit)
2308
+
1929
2309
 
1930
2310
  class WeightedSumSynthesisDefault(HighLevelSynthesisPlugin):
1931
2311
  """Synthesize a :class:`.WeightedSumGate` using the default synthesis algorithm.