qiskit 2.0.3__cp39-abi3-macosx_11_0_arm64.whl → 2.1.0__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 +104 -20
  5. qiskit/circuit/_add_control.py +57 -31
  6. qiskit/circuit/_classical_resource_map.py +4 -0
  7. qiskit/circuit/annotation.py +504 -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 +4 -4
  15. qiskit/circuit/library/arithmetic/multipliers/multiplier.py +2 -2
  16. qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +8 -4
  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 +3 -3
  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 +1 -1
  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 +172 -99
  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 +182 -6
  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/result/sampled_expval.py +3 -1
  115. qiskit/synthesis/__init__.py +10 -0
  116. qiskit/synthesis/arithmetic/__init__.py +1 -1
  117. qiskit/synthesis/arithmetic/adders/__init__.py +1 -0
  118. qiskit/synthesis/arithmetic/adders/draper_qft_adder.py +6 -2
  119. qiskit/synthesis/arithmetic/adders/rv_ripple_carry_adder.py +156 -0
  120. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +14 -126
  121. qiskit/synthesis/discrete_basis/solovay_kitaev.py +161 -121
  122. qiskit/synthesis/evolution/lie_trotter.py +10 -7
  123. qiskit/synthesis/evolution/product_formula.py +10 -7
  124. qiskit/synthesis/evolution/qdrift.py +10 -7
  125. qiskit/synthesis/evolution/suzuki_trotter.py +10 -7
  126. qiskit/synthesis/multi_controlled/__init__.py +4 -0
  127. qiskit/synthesis/multi_controlled/mcx_synthesis.py +402 -178
  128. qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py +14 -15
  129. qiskit/synthesis/qft/qft_decompose_lnn.py +7 -25
  130. qiskit/synthesis/unitary/qsd.py +80 -9
  131. qiskit/transpiler/__init__.py +10 -3
  132. qiskit/transpiler/instruction_durations.py +2 -20
  133. qiskit/transpiler/passes/__init__.py +5 -2
  134. qiskit/transpiler/passes/layout/dense_layout.py +26 -6
  135. qiskit/transpiler/passes/layout/disjoint_utils.py +1 -166
  136. qiskit/transpiler/passes/layout/sabre_layout.py +22 -3
  137. qiskit/transpiler/passes/layout/sabre_pre_layout.py +1 -1
  138. qiskit/transpiler/passes/layout/vf2_layout.py +49 -13
  139. qiskit/transpiler/passes/layout/vf2_utils.py +10 -0
  140. qiskit/transpiler/passes/optimization/__init__.py +1 -1
  141. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +2 -1
  142. qiskit/transpiler/passes/optimization/optimize_clifford_t.py +68 -0
  143. qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +3 -9
  144. qiskit/transpiler/passes/routing/sabre_swap.py +4 -2
  145. qiskit/transpiler/passes/routing/star_prerouting.py +106 -81
  146. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  147. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
  148. qiskit/transpiler/passes/scheduling/padding/__init__.py +1 -0
  149. qiskit/transpiler/passes/scheduling/padding/context_aware_dynamical_decoupling.py +876 -0
  150. qiskit/transpiler/passes/synthesis/__init__.py +1 -0
  151. qiskit/transpiler/passes/synthesis/clifford_unitary_synth_plugin.py +123 -0
  152. qiskit/transpiler/passes/synthesis/hls_plugins.py +494 -93
  153. qiskit/transpiler/passes/synthesis/plugin.py +4 -0
  154. qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +27 -22
  155. qiskit/transpiler/passmanager_config.py +3 -0
  156. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +149 -28
  157. qiskit/transpiler/preset_passmanagers/common.py +101 -0
  158. qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +6 -0
  159. qiskit/transpiler/preset_passmanagers/level3.py +2 -2
  160. qiskit/transpiler/target.py +15 -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.3.dist-info → qiskit-2.1.0.dist-info}/METADATA +7 -6
  174. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/RECORD +178 -169
  175. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/entry_points.txt +6 -0
  176. qiskit/synthesis/discrete_basis/commutator_decompose.py +0 -265
  177. qiskit/synthesis/discrete_basis/gate_sequence.py +0 -421
  178. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/WHEEL +0 -0
  179. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/licenses/LICENSE.txt +0 -0
  180. {qiskit-2.0.3.dist-info → qiskit-2.1.0.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):
@@ -1745,7 +1966,34 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
1745
1966
  # actual PauliEvolutionGate
1746
1967
  return None
1747
1968
 
1748
- algo = high_level_object.synthesis
1969
+ from qiskit.quantum_info import SparsePauliOp, SparseObservable
1970
+
1971
+ # The synthesis function synth_pauli_network_rustiq does not support SparseObservables,
1972
+ # so we need to convert them to SparsePauliOps.
1973
+ if isinstance(high_level_object.operator, SparsePauliOp):
1974
+ pauli_op = high_level_object.operator
1975
+
1976
+ elif isinstance(high_level_object.operator, SparseObservable):
1977
+ pauli_op = SparsePauliOp.from_sparse_observable(high_level_object.operator)
1978
+
1979
+ elif isinstance(high_level_object.operator, list):
1980
+ pauli_op = []
1981
+ for op in high_level_object.operator:
1982
+ if isinstance(op, SparseObservable):
1983
+ pauli_op.append(SparsePauliOp.from_sparse_observable(op))
1984
+ else:
1985
+ pauli_op.append(op)
1986
+
1987
+ else:
1988
+ raise TranspilerError("Invalid PauliEvolutionGate.")
1989
+
1990
+ evo = PauliEvolutionGate(
1991
+ pauli_op,
1992
+ time=high_level_object.time,
1993
+ label=high_level_object.label,
1994
+ synthesis=high_level_object.synthesis,
1995
+ )
1996
+ algo = evo.synthesis
1749
1997
 
1750
1998
  if not isinstance(algo, ProductFormula):
1751
1999
  warnings.warn(
@@ -1759,9 +2007,8 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
1759
2007
  if "preserve_order" in options:
1760
2008
  algo.preserve_order = options["preserve_order"]
1761
2009
 
1762
- num_qubits = high_level_object.num_qubits
1763
- pauli_network = algo.expand(high_level_object)
1764
-
2010
+ num_qubits = evo.num_qubits
2011
+ pauli_network = algo.expand(evo)
1765
2012
  optimize_count = options.get("optimize_count", True)
1766
2013
  preserve_order = options.get("preserve_order", True)
1767
2014
  upto_clifford = options.get("upto_clifford", False)
@@ -1789,9 +2036,17 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
1789
2036
  """
1790
2037
 
1791
2038
  def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
2039
+ # The plugin is triggered based on the name (i.e. for operations called "annotated").
2040
+ # However, we should only do something when the operation is truthfully an AnnotatedOperation.
1792
2041
  if not isinstance(high_level_object, AnnotatedOperation):
1793
2042
  return None
1794
2043
 
2044
+ # Combine the modifiers. If there were no modifiers, or the modifiers magically canceled out,
2045
+ # return the quantum circuit containing the base operation.
2046
+ high_level_object = self._canonicalize_op(high_level_object)
2047
+ if not isinstance(high_level_object, AnnotatedOperation):
2048
+ return self._instruction_to_circuit(high_level_object)
2049
+
1795
2050
  operation = high_level_object
1796
2051
  modifiers = high_level_object.modifiers
1797
2052
 
@@ -1807,67 +2062,110 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
1807
2062
  "The AnnotatedSynthesisDefault plugin should receive data and input_qubits via options."
1808
2063
  )
1809
2064
 
1810
- if len(modifiers) > 0:
1811
- num_ctrl = sum(
1812
- mod.num_ctrl_qubits for mod in modifiers if isinstance(mod, ControlModifier)
1813
- )
1814
- power = sum(mod.power for mod in modifiers if isinstance(mod, PowerModifier))
1815
- is_inverted = sum(1 for mod in modifiers if isinstance(mod, InverseModifier)) % 2
1816
-
1817
- # First, synthesize the base operation of this annotated operation.
1818
- # As this step cannot use any control qubits as auxiliary qubits, we use a dedicated
1819
- # tracker (annotated_tracker).
1820
- # The logic is as follows:
1821
- # - annotated_tracker.disable control qubits
1822
- # - if have power or inverse modifiers, annotated_tracker.set_dirty(base_qubits)
1823
- # - synthesize the base operation using annotated tracker
1824
- # - main_tracker.set_dirty(base_qubits)
1825
- #
1826
- # Note that we need to set the base_qubits to dirty if we have power or inverse
1827
- # modifiers. For power: even if the power is a positive integer (that is, we need
1828
- # to repeat the same circuit multiple times), even if the target is initially at |0>,
1829
- # it will generally not be at |0> after one iteration. For inverse: as we
1830
- # flip the order of operations, we cannot exploit which qubits are at |0> as "viewed from
1831
- # the back of the circuit". If we just have control modifiers, we can use the state
1832
- # of base qubits when synthesizing the controlled operation.
1833
- #
1834
- # In addition, all of the other global qubits that are not a part of the annotated
1835
- # operation can be used as they are in all cases, since we are assuming that all of
1836
- # the synthesis methods preserve the states of ancilla qubits.
1837
- annotated_tracker = tracker.copy()
1838
- control_qubits = input_qubits[:num_ctrl]
1839
- base_qubits = input_qubits[num_ctrl:]
1840
- annotated_tracker.disable(control_qubits) # do not access control qubits
1841
- if power != 0 or is_inverted:
1842
- annotated_tracker.set_dirty(base_qubits)
1843
-
1844
- # Note that synthesize_operation also returns the output qubits on which the
1845
- # operation is defined, however currently the plugin mechanism has no way
1846
- # to return these (and instead the upstream code greedily grabs some ancilla
1847
- # qubits from the circuit). We should refactor the plugin "run" iterface to
1848
- # return the actual ancilla qubits used.
1849
- synthesized_base_op_result = synthesize_operation(
1850
- operation.base_op, input_qubits[num_ctrl:], data, annotated_tracker
1851
- )
2065
+ # The synthesis consists of two steps:
2066
+ # - First, we synthesize the base operation.
2067
+ # - Second, we apply modifiers to this circuit.
2068
+ #
2069
+ # An important optimization (similar to the code in ``add_control.py``) is to synthesize
2070
+ # the base operation with respect to a larger set of "basis" gates, to which the control
2071
+ # logic can be added more efficiently. In addition, we add annotated operations to be
2072
+ # in this larger set, exploiting the fact that adding control to annotated operations
2073
+ # returns a new annotated operation with an extended list of modifiers.
2074
+ #
2075
+ # Note that it is fine for this function to return a circuit with high-level objects
2076
+ # (including annotated operations) as the HighLevelSynthesis transpiler pass will
2077
+ # recursively re-synthesize this circuit, However, we should always guarantee that some
2078
+ # progress is made.
2079
+ basis = set(EFFICIENTLY_CONTROLLED_GATES + ["annotated", "mcx", "qft"])
2080
+
2081
+ base_synthesis_data = HighLevelSynthesisData(
2082
+ hls_config=data.hls_config,
2083
+ hls_plugin_manager=data.hls_plugin_manager,
2084
+ coupling_map=None,
2085
+ target=None,
2086
+ equivalence_library=data.equivalence_library,
2087
+ hls_op_names=data.hls_op_names,
2088
+ device_insts=basis,
2089
+ use_physical_indices=data.use_physical_indices,
2090
+ min_qubits=0,
2091
+ unroll_definitions=data.unroll_definitions,
2092
+ )
1852
2093
 
1853
- # The base operation does not need to be synthesized.
1854
- # For simplicity, we wrap the instruction into a circuit. Note that
1855
- # this should not deteriorate the quality of the result.
1856
- if synthesized_base_op_result is None:
1857
- synthesized_base_op = self._instruction_to_circuit(operation.base_op)
1858
- else:
1859
- synthesized_base_op = QuantumCircuit._from_circuit_data(
1860
- synthesized_base_op_result[0]
1861
- )
1862
- tracker.set_dirty(base_qubits)
2094
+ num_ctrl = sum(mod.num_ctrl_qubits for mod in modifiers if isinstance(mod, ControlModifier))
2095
+ power = sum(mod.power for mod in modifiers if isinstance(mod, PowerModifier))
2096
+ is_inverted = sum(1 for mod in modifiers if isinstance(mod, InverseModifier)) % 2
2097
+
2098
+ # First, synthesize the base operation of this annotated operation.
2099
+ # As this step cannot use any control qubits as auxiliary qubits, we use a dedicated
2100
+ # tracker (annotated_tracker).
2101
+ # The logic is as follows:
2102
+ # - annotated_tracker.disable control qubits
2103
+ # - if have power or inverse modifiers, annotated_tracker.set_dirty(base_qubits)
2104
+ # - synthesize the base operation using annotated tracker
2105
+ # - main_tracker.set_dirty(base_qubits)
2106
+ #
2107
+ # Note that we need to set the base_qubits to dirty if we have power or inverse
2108
+ # modifiers. For power: even if the power is a positive integer (that is, we need
2109
+ # to repeat the same circuit multiple times), even if the target is initially at |0>,
2110
+ # it will generally not be at |0> after one iteration. For inverse: as we
2111
+ # flip the order of operations, we cannot exploit which qubits are at |0> as "viewed from
2112
+ # the back of the circuit". If we just have control modifiers, we can use the state
2113
+ # of base qubits when synthesizing the controlled operation.
2114
+ #
2115
+ # In addition, all of the other global qubits that are not a part of the annotated
2116
+ # operation can be used as they are in all cases, since we are assuming that all of
2117
+ # the synthesis methods preserve the states of ancilla qubits.
2118
+ annotated_tracker = tracker.copy()
2119
+ control_qubits = input_qubits[:num_ctrl]
2120
+ base_qubits = input_qubits[num_ctrl:]
2121
+ annotated_tracker.disable(control_qubits) # do not access control qubits
2122
+ if power != 0 or is_inverted:
2123
+ annotated_tracker.set_dirty(base_qubits)
2124
+
2125
+ # Note that synthesize_operation also returns the output qubits on which the
2126
+ # operation is defined, however currently the plugin mechanism has no way
2127
+ # to return these (and instead the upstream code greedily grabs some ancilla
2128
+ # qubits from the circuit). We should refactor the plugin "run" iterface to
2129
+ # return the actual ancilla qubits used.
2130
+ synthesized_base_op_result = synthesize_operation(
2131
+ operation.base_op, base_qubits, base_synthesis_data, annotated_tracker
2132
+ )
2133
+
2134
+ # The base operation does not need to be synthesized.
2135
+ # For simplicity, we wrap the instruction into a circuit. Note that
2136
+ # this should not deteriorate the quality of the result.
2137
+ if synthesized_base_op_result is None:
2138
+ synthesized_base_op = self._instruction_to_circuit(operation.base_op)
2139
+ else:
2140
+ synthesized_base_op = QuantumCircuit._from_circuit_data(synthesized_base_op_result[0])
2141
+ tracker.set_dirty(base_qubits)
1863
2142
 
2143
+ # As one simple optimization, we apply conjugate decomposition to the circuit obtained
2144
+ # while synthesizing the base operator.
2145
+ conjugate_decomp = self._conjugate_decomposition(synthesized_base_op)
2146
+
2147
+ if conjugate_decomp is None:
2148
+ # Apply annotations to the whole circuit.
1864
2149
  # This step currently does not introduce ancilla qubits. However it makes
1865
2150
  # a lot of sense to allow this in the future.
1866
2151
  synthesized = self._apply_annotations(synthesized_base_op, operation.modifiers)
2152
+ else:
2153
+ # Apply annotations only to the middle part of the circuit.
2154
+ (front, middle, back) = conjugate_decomp
2155
+ synthesized = QuantumCircuit(operation.num_qubits)
2156
+ synthesized.compose(
2157
+ front, synthesized.qubits[num_ctrl : operation.num_qubits], inplace=True
2158
+ )
2159
+ synthesized.compose(
2160
+ self._apply_annotations(middle, operation.modifiers),
2161
+ synthesized.qubits,
2162
+ inplace=True,
2163
+ )
2164
+ synthesized.compose(
2165
+ back, synthesized.qubits[num_ctrl : operation.num_qubits], inplace=True
2166
+ )
1867
2167
 
1868
- return synthesized
1869
-
1870
- return None
2168
+ return synthesized
1871
2169
 
1872
2170
  @staticmethod
1873
2171
  def _apply_annotations(circuit: QuantumCircuit, modifiers: list[Modifier]) -> QuantumCircuit:
@@ -1931,6 +2229,109 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
1931
2229
  circuit.append(op, circuit.qubits, circuit.clbits)
1932
2230
  return circuit
1933
2231
 
2232
+ @staticmethod
2233
+ def _instruction_to_circuit(op: Operation) -> QuantumCircuit:
2234
+ """Wraps a single operation into a quantum circuit."""
2235
+ circuit = QuantumCircuit(op.num_qubits, op.num_clbits)
2236
+ circuit.append(op, circuit.qubits, circuit.clbits)
2237
+ return circuit
2238
+
2239
+ @staticmethod
2240
+ def _canonicalize_op(op: Operation) -> Operation:
2241
+ """
2242
+ Combines recursive annotated operations and canonicalizes modifiers.
2243
+ """
2244
+ cur = op
2245
+ all_modifiers = []
2246
+
2247
+ while isinstance(cur, AnnotatedOperation):
2248
+ all_modifiers.append(cur.modifiers)
2249
+ cur = cur.base_op
2250
+
2251
+ new_modifiers = []
2252
+ for modifiers in all_modifiers[::-1]:
2253
+ new_modifiers.extend(modifiers)
2254
+
2255
+ canonical_modifiers = _canonicalize_modifiers(new_modifiers)
2256
+
2257
+ if not canonical_modifiers:
2258
+ return cur
2259
+
2260
+ return AnnotatedOperation(cur, canonical_modifiers)
2261
+
2262
+ @staticmethod
2263
+ def _are_inverse_ops(inst1: "CircuitInstruction", inst2: "CircuitInstruction"):
2264
+ """A very naive function that checks whether two circuit instructions are inverse of
2265
+ each other. The main use-case covered is a ``QFTGate`` and its inverse, represented as
2266
+ an ``AnnotatedOperation`` with a single ``InverseModifier``.
2267
+ """
2268
+ res = False
2269
+
2270
+ if (
2271
+ inst1.qubits != inst2.qubits
2272
+ or inst1.clbits != inst2.clbits
2273
+ or len(inst1.params) != len(inst2.params)
2274
+ ):
2275
+ return False
2276
+
2277
+ op1 = inst1.operation
2278
+ op2 = inst2.operation
2279
+
2280
+ ann1 = isinstance(op1, AnnotatedOperation)
2281
+ ann2 = isinstance(op2, AnnotatedOperation)
2282
+
2283
+ if not ann1 and not ann2:
2284
+ res = op1 == op2.inverse()
2285
+ elif not ann1 and ann2 and op2.modifiers == [InverseModifier()]:
2286
+ res = op1 == op2.base_op
2287
+ elif not ann2 and ann1 and op1.modifiers == [InverseModifier()]:
2288
+ res = op1.base_op == op2
2289
+
2290
+ return res
2291
+
2292
+ @staticmethod
2293
+ def _conjugate_decomposition(
2294
+ circuit: QuantumCircuit,
2295
+ ) -> tuple[QuantumCircuit, QuantumCircuit, QuantumCircuit] | None:
2296
+ """
2297
+ Decomposes a circuit ``A`` into 3 sub-circuits ``P``, ``Q``, ``R`` such that
2298
+ ``A = P -- Q -- R`` and ``R = P^{-1}``.
2299
+
2300
+ This is accomplished by iteratively finding inverse nodes at the front and at the back of the
2301
+ circuit.
2302
+
2303
+ The function returns ``None`` when ``P`` and ``R`` are empty.
2304
+ """
2305
+ num_gates = circuit.size()
2306
+
2307
+ idx = 0
2308
+ ridx = num_gates - 1
2309
+
2310
+ while True:
2311
+ if idx >= ridx:
2312
+ break
2313
+ if AnnotatedSynthesisDefault._are_inverse_ops(circuit[idx], circuit[ridx]):
2314
+ idx += 1
2315
+ ridx -= 1
2316
+ else:
2317
+ break
2318
+
2319
+ if idx == 0:
2320
+ return None
2321
+
2322
+ front_circuit = circuit.copy_empty_like()
2323
+ front_circuit.global_phase = 0
2324
+ for i in range(0, idx):
2325
+ front_circuit.append(circuit[i])
2326
+ middle_circuit = circuit.copy_empty_like() # inherits the global phase
2327
+ for i in range(idx, ridx + 1):
2328
+ middle_circuit.append(circuit[i])
2329
+ back_circuit = circuit.copy_empty_like()
2330
+ back_circuit.global_phase = 0
2331
+ for i in range(ridx + 1, num_gates):
2332
+ back_circuit.append(circuit[i])
2333
+ return (front_circuit, middle_circuit, back_circuit)
2334
+
1934
2335
 
1935
2336
  class WeightedSumSynthesisDefault(HighLevelSynthesisPlugin):
1936
2337
  """Synthesize a :class:`.WeightedSumGate` using the default synthesis algorithm.