qiskit 1.0.2__cp38-abi3-win32.whl → 1.1.0__cp38-abi3-win32.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 (263) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +27 -16
  3. qiskit/_accelerate.pyd +0 -0
  4. qiskit/_numpy_compat.py +73 -0
  5. qiskit/assembler/__init__.py +5 -10
  6. qiskit/assembler/disassemble.py +5 -6
  7. qiskit/circuit/__init__.py +1061 -232
  8. qiskit/circuit/_classical_resource_map.py +10 -6
  9. qiskit/circuit/_utils.py +18 -8
  10. qiskit/circuit/annotated_operation.py +21 -0
  11. qiskit/circuit/barrier.py +10 -13
  12. qiskit/circuit/bit.py +0 -1
  13. qiskit/circuit/classical/__init__.py +2 -2
  14. qiskit/circuit/classical/expr/__init__.py +39 -5
  15. qiskit/circuit/classical/expr/constructors.py +84 -1
  16. qiskit/circuit/classical/expr/expr.py +83 -13
  17. qiskit/circuit/classical/expr/visitors.py +83 -0
  18. qiskit/circuit/classical/types/__init__.py +5 -4
  19. qiskit/circuit/classicalfunction/__init__.py +1 -0
  20. qiskit/circuit/commutation_checker.py +86 -51
  21. qiskit/circuit/controlflow/_builder_utils.py +9 -1
  22. qiskit/circuit/controlflow/break_loop.py +8 -22
  23. qiskit/circuit/controlflow/builder.py +116 -1
  24. qiskit/circuit/controlflow/continue_loop.py +8 -22
  25. qiskit/circuit/controlflow/control_flow.py +47 -8
  26. qiskit/circuit/controlflow/for_loop.py +8 -23
  27. qiskit/circuit/controlflow/if_else.py +13 -27
  28. qiskit/circuit/controlflow/switch_case.py +14 -21
  29. qiskit/circuit/controlflow/while_loop.py +9 -23
  30. qiskit/circuit/controlledgate.py +2 -2
  31. qiskit/circuit/delay.py +7 -5
  32. qiskit/circuit/gate.py +20 -7
  33. qiskit/circuit/instruction.py +31 -30
  34. qiskit/circuit/instructionset.py +9 -22
  35. qiskit/circuit/library/__init__.py +3 -13
  36. qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
  37. qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
  38. qiskit/circuit/library/blueprintcircuit.py +29 -7
  39. qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
  40. qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
  41. qiskit/circuit/library/generalized_gates/isometry.py +51 -254
  42. qiskit/circuit/library/generalized_gates/pauli.py +2 -2
  43. qiskit/circuit/library/generalized_gates/permutation.py +4 -1
  44. qiskit/circuit/library/generalized_gates/rv.py +15 -11
  45. qiskit/circuit/library/generalized_gates/uc.py +2 -98
  46. qiskit/circuit/library/generalized_gates/unitary.py +9 -4
  47. qiskit/circuit/library/hamiltonian_gate.py +11 -5
  48. qiskit/circuit/library/n_local/efficient_su2.py +5 -5
  49. qiskit/circuit/library/n_local/n_local.py +100 -49
  50. qiskit/circuit/library/n_local/two_local.py +3 -59
  51. qiskit/circuit/library/overlap.py +3 -3
  52. qiskit/circuit/library/phase_oracle.py +1 -1
  53. qiskit/circuit/library/quantum_volume.py +39 -38
  54. qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
  55. qiskit/circuit/library/standard_gates/global_phase.py +4 -2
  56. qiskit/circuit/library/standard_gates/i.py +1 -2
  57. qiskit/circuit/library/standard_gates/iswap.py +1 -2
  58. qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
  59. qiskit/circuit/library/standard_gates/p.py +31 -15
  60. qiskit/circuit/library/standard_gates/r.py +4 -3
  61. qiskit/circuit/library/standard_gates/rx.py +7 -4
  62. qiskit/circuit/library/standard_gates/rxx.py +4 -3
  63. qiskit/circuit/library/standard_gates/ry.py +7 -4
  64. qiskit/circuit/library/standard_gates/ryy.py +4 -3
  65. qiskit/circuit/library/standard_gates/rz.py +7 -4
  66. qiskit/circuit/library/standard_gates/rzx.py +4 -3
  67. qiskit/circuit/library/standard_gates/rzz.py +4 -3
  68. qiskit/circuit/library/standard_gates/s.py +4 -8
  69. qiskit/circuit/library/standard_gates/t.py +2 -4
  70. qiskit/circuit/library/standard_gates/u.py +16 -11
  71. qiskit/circuit/library/standard_gates/u1.py +6 -2
  72. qiskit/circuit/library/standard_gates/u2.py +4 -2
  73. qiskit/circuit/library/standard_gates/u3.py +9 -5
  74. qiskit/circuit/library/standard_gates/x.py +22 -11
  75. qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
  76. qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
  77. qiskit/circuit/library/standard_gates/z.py +1 -2
  78. qiskit/circuit/measure.py +4 -1
  79. qiskit/circuit/operation.py +13 -8
  80. qiskit/circuit/parameter.py +11 -6
  81. qiskit/circuit/quantumcircuit.py +1910 -260
  82. qiskit/circuit/quantumcircuitdata.py +2 -2
  83. qiskit/circuit/reset.py +5 -2
  84. qiskit/circuit/store.py +95 -0
  85. qiskit/compiler/assembler.py +22 -22
  86. qiskit/compiler/transpiler.py +63 -112
  87. qiskit/converters/__init__.py +17 -2
  88. qiskit/converters/circuit_to_dag.py +7 -0
  89. qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
  90. qiskit/converters/circuit_to_gate.py +2 -0
  91. qiskit/converters/circuit_to_instruction.py +22 -0
  92. qiskit/converters/dag_to_circuit.py +4 -0
  93. qiskit/converters/dag_to_dagdependency_v2.py +44 -0
  94. qiskit/dagcircuit/collect_blocks.py +15 -10
  95. qiskit/dagcircuit/dagcircuit.py +434 -124
  96. qiskit/dagcircuit/dagdependency.py +19 -12
  97. qiskit/dagcircuit/dagdependency_v2.py +641 -0
  98. qiskit/dagcircuit/dagdepnode.py +19 -16
  99. qiskit/dagcircuit/dagnode.py +14 -4
  100. qiskit/passmanager/passmanager.py +11 -11
  101. qiskit/primitives/__init__.py +22 -12
  102. qiskit/primitives/backend_estimator.py +3 -5
  103. qiskit/primitives/backend_estimator_v2.py +410 -0
  104. qiskit/primitives/backend_sampler_v2.py +287 -0
  105. qiskit/primitives/base/base_estimator.py +4 -9
  106. qiskit/primitives/base/base_sampler.py +2 -2
  107. qiskit/primitives/containers/__init__.py +6 -4
  108. qiskit/primitives/containers/bit_array.py +293 -2
  109. qiskit/primitives/containers/data_bin.py +123 -50
  110. qiskit/primitives/containers/estimator_pub.py +10 -3
  111. qiskit/primitives/containers/observables_array.py +2 -2
  112. qiskit/primitives/containers/pub_result.py +1 -1
  113. qiskit/primitives/containers/sampler_pub.py +19 -3
  114. qiskit/primitives/containers/sampler_pub_result.py +74 -0
  115. qiskit/primitives/containers/shape.py +4 -4
  116. qiskit/primitives/statevector_estimator.py +4 -4
  117. qiskit/primitives/statevector_sampler.py +7 -12
  118. qiskit/providers/__init__.py +65 -34
  119. qiskit/providers/backend.py +2 -2
  120. qiskit/providers/backend_compat.py +8 -10
  121. qiskit/providers/basic_provider/__init__.py +2 -23
  122. qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
  123. qiskit/providers/basic_provider/basic_simulator.py +81 -21
  124. qiskit/providers/fake_provider/__init__.py +1 -1
  125. qiskit/providers/fake_provider/fake_1q.py +1 -1
  126. qiskit/providers/fake_provider/fake_backend.py +3 -408
  127. qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
  128. qiskit/providers/models/__init__.py +2 -2
  129. qiskit/providers/provider.py +16 -0
  130. qiskit/pulse/builder.py +4 -1
  131. qiskit/pulse/parameter_manager.py +60 -4
  132. qiskit/pulse/schedule.py +29 -13
  133. qiskit/pulse/utils.py +61 -20
  134. qiskit/qasm2/__init__.py +1 -5
  135. qiskit/qasm2/parse.py +1 -4
  136. qiskit/qasm3/__init__.py +42 -5
  137. qiskit/qasm3/ast.py +19 -0
  138. qiskit/qasm3/exporter.py +178 -106
  139. qiskit/qasm3/printer.py +27 -5
  140. qiskit/qobj/converters/pulse_instruction.py +6 -6
  141. qiskit/qpy/__init__.py +299 -67
  142. qiskit/qpy/binary_io/circuits.py +216 -47
  143. qiskit/qpy/binary_io/schedules.py +42 -36
  144. qiskit/qpy/binary_io/value.py +201 -22
  145. qiskit/qpy/common.py +1 -1
  146. qiskit/qpy/exceptions.py +20 -0
  147. qiskit/qpy/formats.py +29 -0
  148. qiskit/qpy/type_keys.py +21 -0
  149. qiskit/quantum_info/analysis/distance.py +3 -3
  150. qiskit/quantum_info/analysis/make_observable.py +2 -1
  151. qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
  152. qiskit/quantum_info/operators/channel/chi.py +9 -8
  153. qiskit/quantum_info/operators/channel/choi.py +10 -9
  154. qiskit/quantum_info/operators/channel/kraus.py +2 -1
  155. qiskit/quantum_info/operators/channel/ptm.py +10 -9
  156. qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
  157. qiskit/quantum_info/operators/channel/stinespring.py +2 -1
  158. qiskit/quantum_info/operators/channel/superop.py +12 -11
  159. qiskit/quantum_info/operators/channel/transformations.py +12 -11
  160. qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
  161. qiskit/quantum_info/operators/operator.py +43 -30
  162. qiskit/quantum_info/operators/scalar_op.py +10 -9
  163. qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
  164. qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
  165. qiskit/quantum_info/operators/symplectic/pauli.py +53 -6
  166. qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
  167. qiskit/quantum_info/operators/symplectic/random.py +3 -2
  168. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +61 -36
  169. qiskit/quantum_info/states/densitymatrix.py +13 -13
  170. qiskit/quantum_info/states/stabilizerstate.py +3 -3
  171. qiskit/quantum_info/states/statevector.py +14 -13
  172. qiskit/quantum_info/states/utils.py +5 -3
  173. qiskit/result/__init__.py +6 -0
  174. qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
  175. qiskit/result/mitigation/local_readout_mitigator.py +2 -1
  176. qiskit/result/mitigation/utils.py +3 -2
  177. qiskit/scheduler/__init__.py +10 -1
  178. qiskit/scheduler/methods/__init__.py +1 -8
  179. qiskit/synthesis/__init__.py +3 -6
  180. qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
  181. qiskit/synthesis/evolution/lie_trotter.py +7 -14
  182. qiskit/synthesis/evolution/qdrift.py +3 -4
  183. qiskit/synthesis/linear/cnot_synth.py +1 -3
  184. qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
  185. qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
  186. qiskit/synthesis/permutation/__init__.py +1 -0
  187. qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
  188. qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
  189. qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
  190. qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
  191. qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
  192. qiskit/synthesis/unitary/aqc/__init__.py +1 -1
  193. qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
  194. qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
  195. qiskit/synthesis/unitary/qsd.py +3 -2
  196. qiskit/transpiler/__init__.py +7 -3
  197. qiskit/transpiler/layout.py +140 -61
  198. qiskit/transpiler/passes/__init__.py +10 -2
  199. qiskit/transpiler/passes/basis/basis_translator.py +9 -4
  200. qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
  201. qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
  202. qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
  203. qiskit/transpiler/passes/layout/apply_layout.py +8 -3
  204. qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
  205. qiskit/transpiler/passes/layout/set_layout.py +1 -1
  206. qiskit/transpiler/passes/optimization/__init__.py +2 -0
  207. qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
  208. qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
  209. qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
  210. qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
  211. qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
  212. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
  213. qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
  214. qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
  215. qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
  216. qiskit/transpiler/passes/routing/__init__.py +1 -0
  217. qiskit/transpiler/passes/routing/basic_swap.py +13 -2
  218. qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +8 -1
  219. qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
  220. qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
  221. qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
  222. qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
  223. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  224. qiskit/transpiler/passes/scheduling/alap.py +1 -2
  225. qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
  226. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
  227. qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
  228. qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
  229. qiskit/transpiler/passes/scheduling/asap.py +1 -2
  230. qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
  231. qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
  232. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
  233. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
  234. qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
  235. qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
  236. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
  237. qiskit/transpiler/passes/utils/gates_basis.py +3 -3
  238. qiskit/transpiler/passmanager.py +44 -1
  239. qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
  240. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
  241. qiskit/transpiler/preset_passmanagers/common.py +4 -6
  242. qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
  243. qiskit/utils/__init__.py +3 -2
  244. qiskit/utils/optionals.py +6 -2
  245. qiskit/utils/parallel.py +24 -15
  246. qiskit/visualization/array.py +1 -1
  247. qiskit/visualization/bloch.py +2 -3
  248. qiskit/visualization/circuit/matplotlib.py +44 -14
  249. qiskit/visualization/circuit/text.py +38 -18
  250. qiskit/visualization/counts_visualization.py +3 -6
  251. qiskit/visualization/dag_visualization.py +6 -7
  252. qiskit/visualization/gate_map.py +9 -1
  253. qiskit/visualization/pulse_v2/interface.py +8 -3
  254. qiskit/visualization/state_visualization.py +3 -2
  255. qiskit/visualization/timeline/interface.py +18 -8
  256. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/METADATA +12 -8
  257. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/RECORD +261 -251
  258. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/WHEEL +1 -1
  259. qiskit/_qasm2.pyd +0 -0
  260. qiskit/_qasm3.pyd +0 -0
  261. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/LICENSE.txt +0 -0
  262. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/entry_points.txt +0 -0
  263. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/top_level.txt +0 -0
@@ -29,26 +29,58 @@ import math
29
29
  import io
30
30
  import base64
31
31
  import warnings
32
- from typing import ClassVar, Optional, Type
32
+ from typing import Optional, Type, TYPE_CHECKING
33
33
 
34
34
  import logging
35
35
 
36
36
  import numpy as np
37
37
 
38
38
  from qiskit.circuit import QuantumRegister, QuantumCircuit, Gate
39
- from qiskit.circuit.library.standard_gates import CXGate, RXGate, RYGate, RZGate
39
+ from qiskit.circuit.library.standard_gates import (
40
+ CXGate,
41
+ U3Gate,
42
+ U2Gate,
43
+ U1Gate,
44
+ UGate,
45
+ PhaseGate,
46
+ RXGate,
47
+ RYGate,
48
+ RZGate,
49
+ SXGate,
50
+ XGate,
51
+ RGate,
52
+ )
40
53
  from qiskit.exceptions import QiskitError
41
54
  from qiskit.quantum_info.operators import Operator
42
- from qiskit.synthesis.two_qubit.weyl import transform_to_magic_basis
43
55
  from qiskit.synthesis.one_qubit.one_qubit_decompose import (
44
56
  OneQubitEulerDecomposer,
45
57
  DEFAULT_ATOL,
46
58
  )
59
+ from qiskit.utils.deprecation import deprecate_func
47
60
  from qiskit._accelerate import two_qubit_decompose
48
61
 
62
+ if TYPE_CHECKING:
63
+ from qiskit.dagcircuit.dagcircuit import DAGCircuit
64
+
49
65
  logger = logging.getLogger(__name__)
50
66
 
51
67
 
68
+ GATE_NAME_MAP = {
69
+ "cx": CXGate,
70
+ "rx": RXGate,
71
+ "sx": SXGate,
72
+ "x": XGate,
73
+ "rz": RZGate,
74
+ "u": UGate,
75
+ "p": PhaseGate,
76
+ "u1": U1Gate,
77
+ "u2": U2Gate,
78
+ "u3": U3Gate,
79
+ "ry": RYGate,
80
+ "r": RGate,
81
+ }
82
+
83
+
52
84
  def decompose_two_qubit_product_gate(special_unitary_matrix: np.ndarray):
53
85
  r"""Decompose :math:`U = U_l \otimes U_r` where :math:`U \in SU(4)`,
54
86
  and :math:`U_l,~U_r \in SU(2)`.
@@ -118,13 +150,9 @@ class TwoQubitWeylDecomposition:
118
150
 
119
151
  \pi /4 \geq a \geq b \geq |c|
120
152
 
121
- This is an abstract factory class that instantiates itself as specialized subclasses based on
122
- the fidelity, such that the approximation error from specialization has an average gate fidelity
123
- at least as high as requested. The specialized subclasses have unique canonical representations
124
- thus avoiding problems of numerical stability.
125
-
126
- Passing non-None fidelity to specializations is treated as an assertion, raising QiskitError if
127
- forcing the specialization is more approximate than asserted.
153
+ This class avoids some problems of numerical instability near high-symmetry loci within the Weyl
154
+ chamber. If there is a high-symmetry gate "nearby" (in terms of the requested average gate fidelity),
155
+ then it return a canonicalized decomposition of that high-symmetry gate.
128
156
 
129
157
  References:
130
158
  1. Cross, A. W., Bishop, L. S., Sheldon, S., Nation, P. D. & Gambetta, J. M.,
@@ -151,237 +179,30 @@ class TwoQubitWeylDecomposition:
151
179
  requested_fidelity: Optional[float] # None means no automatic specialization
152
180
  calculated_fidelity: float # Fidelity after specialization
153
181
 
154
- _original_decomposition: "TwoQubitWeylDecomposition"
155
- _is_flipped_from_original: bool # The approx is closest to a Weyl reflection of the original?
156
-
157
- _default_1q_basis: ClassVar[str] = "ZYZ" # Default one qubit basis (explicit parameterization)
158
-
159
- def __init_subclass__(cls, **kwargs):
160
- """Subclasses should be concrete, not factories.
161
-
162
- Make explicitly-instantiated subclass __new__ call base __new__ with fidelity=None"""
163
- super().__init_subclass__(**kwargs)
164
- cls.__new__ = lambda cls, *a, fidelity=None, **k: TwoQubitWeylDecomposition.__new__(
165
- cls, *a, fidelity=None, **k
166
- )
167
-
168
- @staticmethod
169
- def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9), _unpickling=False):
170
- """Perform the Weyl chamber decomposition, and optionally choose a specialized subclass."""
171
-
172
- # The flip into the Weyl Chamber is described in B. Kraus and J. I. Cirac, Phys. Rev. A 63,
173
- # 062309 (2001).
174
- #
175
- # FIXME: There's a cleaner-seeming method based on choosing branch cuts carefully, in Andrew
176
- # M. Childs, Henry L. Haselgrove, and Michael A. Nielsen, Phys. Rev. A 68, 052311, but I
177
- # wasn't able to get that to work.
178
- #
179
- # The overall decomposition scheme is taken from Drury and Love, arXiv:0806.4015 [quant-ph].
180
-
181
- if _unpickling:
182
- return super().__new__(cls)
183
-
184
- pi = np.pi
185
- pi2 = np.pi / 2
186
- pi4 = np.pi / 4
187
-
188
- # Make U be in SU(4)
189
- U = np.array(unitary_matrix, dtype=complex, copy=True)
190
- detU = np.linalg.det(U)
191
- U *= detU ** (-0.25)
192
- global_phase = cmath.phase(detU) / 4
193
-
194
- Up = transform_to_magic_basis(U, reverse=True)
195
- M2 = Up.T.dot(Up)
196
-
197
- # M2 is a symmetric complex matrix. We need to decompose it as M2 = P D P^T where
198
- # P ∈ SO(4), D is diagonal with unit-magnitude elements.
199
- #
200
- # We can't use raw `eig` directly because it isn't guaranteed to give us real or othogonal
201
- # eigenvectors. Instead, since `M2` is complex-symmetric,
202
- # M2 = A + iB
203
- # for real-symmetric `A` and `B`, and as
204
- # M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1
205
- # we must have `A` and `B` commute, and consequently they are simultaneously diagonalizable.
206
- # Mixing them together _should_ account for any degeneracy problems, but it's not
207
- # guaranteed, so we repeat it a little bit. The fixed seed is to make failures
208
- # deterministic; the value is not important.
209
- state = np.random.default_rng(2020)
210
- for _ in range(100): # FIXME: this randomized algorithm is horrendous
211
- M2real = state.normal() * M2.real + state.normal() * M2.imag
212
- _, P = np.linalg.eigh(M2real)
213
- D = P.T.dot(M2).dot(P).diagonal()
214
- if np.allclose(P.dot(np.diag(D)).dot(P.T), M2, rtol=0, atol=1.0e-13):
215
- break
216
- else:
217
- raise QiskitError(
218
- "TwoQubitWeylDecomposition: failed to diagonalize M2."
219
- " Please report this at https://github.com/Qiskit/qiskit-terra/issues/4159."
220
- f" Input: {U.tolist()}"
221
- )
222
-
223
- d = -np.angle(D) / 2
224
- d[3] = -d[0] - d[1] - d[2]
225
- cs = np.mod((d[:3] + d[3]) / 2, 2 * np.pi)
226
-
227
- # Reorder the eigenvalues to get in the Weyl chamber
228
- cstemp = np.mod(cs, pi2)
229
- np.minimum(cstemp, pi2 - cstemp, cstemp)
230
- order = np.argsort(cstemp)[[1, 2, 0]]
231
- cs = cs[order]
232
- d[:3] = d[order]
233
- P[:, :3] = P[:, order]
234
-
235
- # Fix the sign of P to be in SO(4)
236
- if np.real(np.linalg.det(P)) < 0:
237
- P[:, -1] = -P[:, -1]
238
-
239
- # Find K1, K2 so that U = K1.A.K2, with K being product of single-qubit unitaries
240
- K1 = transform_to_magic_basis(Up @ P @ np.diag(np.exp(1j * d)))
241
- K2 = transform_to_magic_basis(P.T)
242
-
243
- K1l, K1r, phase_l = decompose_two_qubit_product_gate(K1)
244
- K2l, K2r, phase_r = decompose_two_qubit_product_gate(K2)
245
- global_phase += phase_l + phase_r
246
-
247
- K1l = K1l.copy()
248
-
249
- # Flip into Weyl chamber
250
- if cs[0] > pi2:
251
- cs[0] -= 3 * pi2
252
- K1l = K1l.dot(_ipy)
253
- K1r = K1r.dot(_ipy)
254
- global_phase += pi2
255
- if cs[1] > pi2:
256
- cs[1] -= 3 * pi2
257
- K1l = K1l.dot(_ipx)
258
- K1r = K1r.dot(_ipx)
259
- global_phase += pi2
260
- conjs = 0
261
- if cs[0] > pi4:
262
- cs[0] = pi2 - cs[0]
263
- K1l = K1l.dot(_ipy)
264
- K2r = _ipy.dot(K2r)
265
- conjs += 1
266
- global_phase -= pi2
267
- if cs[1] > pi4:
268
- cs[1] = pi2 - cs[1]
269
- K1l = K1l.dot(_ipx)
270
- K2r = _ipx.dot(K2r)
271
- conjs += 1
272
- global_phase += pi2
273
- if conjs == 1:
274
- global_phase -= pi
275
- if cs[2] > pi2:
276
- cs[2] -= 3 * pi2
277
- K1l = K1l.dot(_ipz)
278
- K1r = K1r.dot(_ipz)
279
- global_phase += pi2
280
- if conjs == 1:
281
- global_phase -= pi
282
- if conjs == 1:
283
- cs[2] = pi2 - cs[2]
284
- K1l = K1l.dot(_ipz)
285
- K2r = _ipz.dot(K2r)
286
- global_phase += pi2
287
- if cs[2] > pi4:
288
- cs[2] -= pi2
289
- K1l = K1l.dot(_ipz)
290
- K1r = K1r.dot(_ipz)
291
- global_phase -= pi2
292
-
293
- a, b, c = cs[1], cs[0], cs[2]
294
-
295
- # Save the non-specialized decomposition for later comparison
296
- od = super().__new__(TwoQubitWeylDecomposition)
297
- od.a = a
298
- od.b = b
299
- od.c = c
300
- od.K1l = K1l
301
- od.K1r = K1r
302
- od.K2l = K2l
303
- od.K2r = K2r
304
- od.global_phase = global_phase
305
- od.requested_fidelity = fidelity
306
- od.calculated_fidelity = 1.0
307
- od.unitary_matrix = np.array(unitary_matrix, dtype=complex, copy=True)
308
- od.unitary_matrix.setflags(write=False)
309
- od._original_decomposition = None
310
- od._is_flipped_from_original = False
311
-
312
- def is_close(ap, bp, cp):
313
- da, db, dc = a - ap, b - bp, c - cp
314
- tr = 4 * complex(
315
- math.cos(da) * math.cos(db) * math.cos(dc),
316
- math.sin(da) * math.sin(db) * math.sin(dc),
317
- )
318
- fid = trace_to_fid(tr)
319
- return fid >= fidelity
320
-
321
- if fidelity is None: # Don't specialize if None
322
- instance = super().__new__(
323
- TwoQubitWeylGeneral if cls is TwoQubitWeylDecomposition else cls
324
- )
325
- elif is_close(0, 0, 0):
326
- instance = super().__new__(TwoQubitWeylIdEquiv)
327
- elif is_close(pi4, pi4, pi4) or is_close(pi4, pi4, -pi4):
328
- instance = super().__new__(TwoQubitWeylSWAPEquiv)
329
- elif (lambda x: is_close(x, x, x))(_closest_partial_swap(a, b, c)):
330
- instance = super().__new__(TwoQubitWeylPartialSWAPEquiv)
331
- elif (lambda x: is_close(x, x, -x))(_closest_partial_swap(a, b, -c)):
332
- instance = super().__new__(TwoQubitWeylPartialSWAPFlipEquiv)
333
- elif is_close(a, 0, 0):
334
- instance = super().__new__(TwoQubitWeylControlledEquiv)
335
- elif is_close(pi4, pi4, c):
336
- instance = super().__new__(TwoQubitWeylMirrorControlledEquiv)
337
- elif is_close((a + b) / 2, (a + b) / 2, c):
338
- instance = super().__new__(TwoQubitWeylfSimaabEquiv)
339
- elif is_close(a, (b + c) / 2, (b + c) / 2):
340
- instance = super().__new__(TwoQubitWeylfSimabbEquiv)
341
- elif is_close(a, (b - c) / 2, (c - b) / 2):
342
- instance = super().__new__(TwoQubitWeylfSimabmbEquiv)
343
- else:
344
- instance = super().__new__(TwoQubitWeylGeneral)
345
-
346
- instance._original_decomposition = od
347
- return instance
182
+ _specializations = two_qubit_decompose.Specialization
348
183
 
349
184
  def __init__(
350
185
  self,
351
- unitary_matrix: list[list[complex]] | np.ndarray[complex],
352
- fidelity: float | None = None,
186
+ unitary_matrix: np.ndarray,
187
+ fidelity: float | None = 1.0 - 1.0e-9,
188
+ *,
189
+ _specialization: two_qubit_decompose.Specialization | None = None,
353
190
  ):
354
- """
355
- Args:
356
- unitary_matrix: The unitary to decompose.
357
- fidelity: The target fidelity of the decomposed operation.
358
- """
359
- del unitary_matrix # unused in __init__ (used in new)
360
- od = self._original_decomposition
361
- self.a, self.b, self.c = od.a, od.b, od.c
362
- self.K1l, self.K1r = od.K1l, od.K1r
363
- self.K2l, self.K2r = od.K2l, od.K2r
364
- self.global_phase = od.global_phase
365
- self.unitary_matrix = od.unitary_matrix
191
+ unitary_matrix = np.asarray(unitary_matrix, dtype=complex)
192
+ self._inner_decomposition = two_qubit_decompose.TwoQubitWeylDecomposition(
193
+ unitary_matrix, fidelity=fidelity, _specialization=_specialization
194
+ )
195
+ self.a = self._inner_decomposition.a
196
+ self.b = self._inner_decomposition.b
197
+ self.c = self._inner_decomposition.c
198
+ self.global_phase = self._inner_decomposition.global_phase
199
+ self.K1l = self._inner_decomposition.K1l
200
+ self.K1r = self._inner_decomposition.K1r
201
+ self.K2l = self._inner_decomposition.K2l
202
+ self.K2r = self._inner_decomposition.K2r
203
+ self.unitary_matrix = unitary_matrix
366
204
  self.requested_fidelity = fidelity
367
- self._is_flipped_from_original = False
368
- self.specialize()
369
-
370
- # Update the phase after specialization:
371
- if self._is_flipped_from_original:
372
- da, db, dc = (np.pi / 2 - od.a) - self.a, od.b - self.b, -od.c - self.c
373
- tr = 4 * complex(
374
- math.cos(da) * math.cos(db) * math.cos(dc),
375
- math.sin(da) * math.sin(db) * math.sin(dc),
376
- )
377
- else:
378
- da, db, dc = od.a - self.a, od.b - self.b, od.c - self.c
379
- tr = 4 * complex(
380
- math.cos(da) * math.cos(db) * math.cos(dc),
381
- math.sin(da) * math.sin(db) * math.sin(dc),
382
- )
383
- self.global_phase += cmath.phase(tr)
384
- self.calculated_fidelity = trace_to_fid(tr)
205
+ self.calculated_fidelity = self._inner_decomposition.calculated_fidelity
385
206
  if logger.isEnabledFor(logging.DEBUG):
386
207
  actual_fidelity = self.actual_fidelity()
387
208
  logger.debug(
@@ -395,62 +216,34 @@ class TwoQubitWeylDecomposition:
395
216
  "Requested fidelity different from actual by %s",
396
217
  self.calculated_fidelity - actual_fidelity,
397
218
  )
398
- if self.requested_fidelity and self.calculated_fidelity + 1.0e-13 < self.requested_fidelity:
399
- raise QiskitError(
400
- f"{self.__class__.__name__}: "
401
- f"calculated fidelity: {self.calculated_fidelity} "
402
- f"is worse than requested fidelity: {self.requested_fidelity}."
403
- )
404
219
 
220
+ @deprecate_func(since="1.1.0", removal_timeline="in the 2.0.0 release")
405
221
  def specialize(self):
406
- """Make changes to the decomposition to comply with any specialization."""
222
+ """Make changes to the decomposition to comply with any specializations.
407
223
 
408
- # Do update a, b, c, k1l, k1r, k2l, k2r, _is_flipped_from_original to round to the
409
- # specialization. Do not update the global phase, since this gets done in generic
410
- # __init__()
224
+ This method will always raise a ``NotImplementedError`` because
225
+ there are no specializations to comply with in the current implementation.
226
+ """
411
227
  raise NotImplementedError
412
228
 
413
229
  def circuit(
414
230
  self, *, euler_basis: str | None = None, simplify: bool = False, atol: float = DEFAULT_ATOL
415
231
  ) -> QuantumCircuit:
416
232
  """Returns Weyl decomposition in circuit form."""
417
-
418
- # simplify, atol arguments are passed to OneQubitEulerDecomposer
419
- if euler_basis is None:
420
- euler_basis = self._default_1q_basis
421
- oneq_decompose = OneQubitEulerDecomposer(euler_basis)
422
- c1l, c1r, c2l, c2r = (
423
- oneq_decompose(k, simplify=simplify, atol=atol)
424
- for k in (self.K1l, self.K1r, self.K2l, self.K2r)
233
+ circuit_sequence = self._inner_decomposition.circuit(
234
+ euler_basis=euler_basis, simplify=simplify, atol=atol
425
235
  )
426
- circ = QuantumCircuit(2, global_phase=self.global_phase)
427
- circ.compose(c2r, [0], inplace=True)
428
- circ.compose(c2l, [1], inplace=True)
429
- self._weyl_gate(simplify, circ, atol)
430
- circ.compose(c1r, [0], inplace=True)
431
- circ.compose(c1l, [1], inplace=True)
236
+ circ = QuantumCircuit(2, global_phase=circuit_sequence.global_phase)
237
+ for name, params, qubits in circuit_sequence:
238
+ getattr(circ, name)(*params, *qubits)
432
239
  return circ
433
240
 
434
- def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
435
- """Appends U_d(a, b, c) to the circuit.
436
-
437
- Can be overridden in subclasses for special cases"""
438
- if not simplify or abs(self.a) > atol:
439
- circ.rxx(-self.a * 2, 0, 1)
440
- if not simplify or abs(self.b) > atol:
441
- circ.ryy(-self.b * 2, 0, 1)
442
- if not simplify or abs(self.c) > atol:
443
- circ.rzz(-self.c * 2, 0, 1)
444
-
445
241
  def actual_fidelity(self, **kwargs) -> float:
446
242
  """Calculates the actual fidelity of the decomposed circuit to the input unitary."""
447
243
  circ = self.circuit(**kwargs)
448
244
  trace = np.trace(Operator(circ).data.T.conj() @ self.unitary_matrix)
449
245
  return trace_to_fid(trace)
450
246
 
451
- def __getnewargs_ex__(self):
452
- return (self.unitary_matrix,), {"_unpickling": True}
453
-
454
247
  def __repr__(self):
455
248
  """Represent with enough precision to allow copy-paste debugging of all corner cases"""
456
249
  prefix = f"{type(self).__qualname__}.from_bytes("
@@ -461,12 +254,15 @@ class TwoQubitWeylDecomposition:
461
254
  b64ascii[-1] += ","
462
255
  pretty = [f"# {x.rstrip()}" for x in str(self).splitlines()]
463
256
  indent = "\n" + " " * 4
257
+ specialization_variant = str(self._inner_decomposition.specialization).split(".")[1]
258
+ specialization_repr = f"{type(self).__qualname__}._specializations.{specialization_variant}"
464
259
  lines = (
465
260
  [prefix]
466
261
  + pretty
467
262
  + b64ascii
468
263
  + [
469
264
  f"requested_fidelity={self.requested_fidelity},",
265
+ f"_specialization={specialization_repr},",
470
266
  f"calculated_fidelity={self.calculated_fidelity},",
471
267
  f"actual_fidelity={self.actual_fidelity()},",
472
268
  f"abc={(self.a, self.b, self.c)})",
@@ -476,7 +272,12 @@ class TwoQubitWeylDecomposition:
476
272
 
477
273
  @classmethod
478
274
  def from_bytes(
479
- cls, bytes_in: bytes, *, requested_fidelity: float, **kwargs
275
+ cls,
276
+ bytes_in: bytes,
277
+ *,
278
+ requested_fidelity: float,
279
+ _specialization: two_qubit_decompose.Specialization | None = None,
280
+ **kwargs,
480
281
  ) -> "TwoQubitWeylDecomposition":
481
282
  """Decode bytes into :class:`.TwoQubitWeylDecomposition`."""
482
283
  # Used by __repr__
@@ -484,121 +285,15 @@ class TwoQubitWeylDecomposition:
484
285
  b64 = base64.decodebytes(bytes_in)
485
286
  with io.BytesIO(b64) as f:
486
287
  arr = np.load(f, allow_pickle=False)
487
- return cls(arr, fidelity=requested_fidelity)
288
+ return cls(arr, fidelity=requested_fidelity, _specialization=_specialization)
488
289
 
489
290
  def __str__(self):
490
- pre = f"{self.__class__.__name__}(\n\t"
291
+ specialization = str(self._inner_decomposition.specialization).split(".")[1]
292
+ pre = f"{self.__class__.__name__} [specialization={specialization}] (\n\t"
491
293
  circ_indent = "\n\t".join(self.circuit(simplify=True).draw("text").lines(-1))
492
294
  return f"{pre}{circ_indent}\n)"
493
295
 
494
296
 
495
- class TwoQubitWeylIdEquiv(TwoQubitWeylDecomposition):
496
- r""":math:`U \sim U_d(0,0,0) \sim Id`
497
-
498
- This gate binds 0 parameters, we make it canonical by setting
499
- :math:`K2_l = Id` , :math:`K2_r = Id`.
500
- """
501
-
502
- def specialize(self):
503
- self.a = self.b = self.c = 0.0
504
- self.K1l = self.K1l @ self.K2l
505
- self.K1r = self.K1r @ self.K2r
506
- self.K2l = _id.copy()
507
- self.K2r = _id.copy()
508
-
509
-
510
- class TwoQubitWeylSWAPEquiv(TwoQubitWeylDecomposition):
511
- r""":math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4) \sim \text{SWAP}`
512
-
513
- This gate binds 0 parameters, we make it canonical by setting
514
- :math:`K2_l = Id` , :math:`K2_r = Id`.
515
- """
516
-
517
- def specialize(self):
518
- if self.c > 0:
519
- self.K1l = self.K1l @ self.K2r
520
- self.K1r = self.K1r @ self.K2l
521
- else:
522
- self._is_flipped_from_original = True
523
- self.K1l = self.K1l @ _ipz @ self.K2r
524
- self.K1r = self.K1r @ _ipz @ self.K2l
525
- self.global_phase = self.global_phase + np.pi / 2
526
- self.a = self.b = self.c = np.pi / 4
527
- self.K2l = _id.copy()
528
- self.K2r = _id.copy()
529
-
530
- def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
531
- del self, simplify, atol # unused
532
- circ.swap(0, 1)
533
- circ.global_phase -= 3 * np.pi / 4
534
-
535
-
536
- def _closest_partial_swap(a, b, c) -> float:
537
- r"""A good approximation to the best value x to get the minimum
538
- trace distance for :math:`U_d(x, x, x)` from :math:`U_d(a, b, c)`.
539
- """
540
- m = (a + b + c) / 3
541
- am, bm, cm = a - m, b - m, c - m
542
- ab, bc, ca = a - b, b - c, c - a
543
-
544
- return m + am * bm * cm * (6 + ab * ab + bc * bc + ca * ca) / 18
545
-
546
-
547
- class TwoQubitWeylPartialSWAPEquiv(TwoQubitWeylDecomposition):
548
- r""":math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim \text{SWAP}^\alpha`
549
- This gate binds 3 parameters, we make it canonical by setting:
550
- :math:`K2_l = Id`.
551
- """
552
-
553
- def specialize(self):
554
- self.a = self.b = self.c = _closest_partial_swap(self.a, self.b, self.c)
555
- self.K1l = self.K1l @ self.K2l
556
- self.K1r = self.K1r @ self.K2l
557
- self.K2r = self.K2l.T.conj() @ self.K2r
558
- self.K2l = _id.copy()
559
-
560
-
561
- class TwoQubitWeylPartialSWAPFlipEquiv(TwoQubitWeylDecomposition):
562
- r""":math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim \text{SWAP}^\alpha`
563
- (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv
564
- similar to how :math:`x = (\pm \sqrt(x))^2`)
565
- This gate binds 3 parameters, we make it canonical by setting:
566
- :math:`K2_l = Id`.
567
- """
568
-
569
- def specialize(self):
570
- self.a = self.b = _closest_partial_swap(self.a, self.b, -self.c)
571
- self.c = -self.a
572
- self.K1l = self.K1l @ self.K2l
573
- self.K1r = self.K1r @ _ipz @ self.K2l @ _ipz
574
- self.K2r = _ipz @ self.K2l.T.conj() @ _ipz @ self.K2r
575
- self.K2l = _id.copy()
576
-
577
-
578
- _oneq_xyx = OneQubitEulerDecomposer("XYX")
579
- _oneq_zyz = OneQubitEulerDecomposer("ZYZ")
580
-
581
-
582
- class TwoQubitWeylControlledEquiv(TwoQubitWeylDecomposition):
583
- r""":math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}`
584
- This gate binds 4 parameters, we make it canonical by setting:
585
- :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` ,
586
- :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` .
587
- """
588
-
589
- _default_1q_basis = "XYX"
590
-
591
- def specialize(self):
592
- self.b = self.c = 0
593
- k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
594
- k2rtheta, k2rphi, k2rlambda, k2rphase = _oneq_xyx.angles_and_phase(self.K2r)
595
- self.global_phase += k2lphase + k2rphase
596
- self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
597
- self.K1r = self.K1r @ np.asarray(RXGate(k2rphi))
598
- self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
599
- self.K2r = np.asarray(RYGate(k2rtheta)) @ np.asarray(RXGate(k2rlambda))
600
-
601
-
602
297
  class TwoQubitControlledUDecomposer:
603
298
  r"""Decompose two-qubit unitary in terms of a desired
604
299
  :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}`
@@ -627,18 +322,23 @@ class TwoQubitControlledUDecomposer:
627
322
 
628
323
  circ = QuantumCircuit(2)
629
324
  circ.rxx(test_angle, 0, 1)
630
- decomposer_rxx = TwoQubitWeylControlledEquiv(Operator(circ).data)
325
+ decomposer_rxx = TwoQubitWeylDecomposition(
326
+ Operator(circ).data,
327
+ fidelity=None,
328
+ _specialization=two_qubit_decompose.Specialization.ControlledEquiv,
329
+ )
631
330
 
632
331
  circ = QuantumCircuit(2)
633
332
  circ.append(rxx_equivalent_gate(test_angle), qargs=[0, 1])
634
- decomposer_equiv = TwoQubitWeylControlledEquiv(Operator(circ).data)
333
+ decomposer_equiv = TwoQubitWeylDecomposition(
334
+ Operator(circ).data,
335
+ fidelity=None,
336
+ _specialization=two_qubit_decompose.Specialization.ControlledEquiv,
337
+ )
635
338
 
636
339
  scale = decomposer_rxx.a / decomposer_equiv.a
637
340
 
638
- if (
639
- not isinstance(decomp, TwoQubitWeylControlledEquiv)
640
- or abs(decomp.a * 2 - test_angle / scale) > atol
641
- ):
341
+ if abs(decomp.a * 2 - test_angle / scale) > atol:
642
342
  raise QiskitError(
643
343
  f"{rxx_equivalent_gate.__name__} is not equivalent to an RXXGate."
644
344
  )
@@ -706,7 +406,7 @@ class TwoQubitControlledUDecomposer:
706
406
 
707
407
  circ = QuantumCircuit(2)
708
408
  circ.append(self.rxx_equivalent_gate(self.scale * angle), qargs=[0, 1])
709
- decomposer_inv = TwoQubitWeylControlledEquiv(Operator(circ).data)
409
+ decomposer_inv = TwoQubitWeylDecomposition(Operator(circ).data)
710
410
 
711
411
  oneq_decompose = OneQubitEulerDecomposer("ZYZ")
712
412
 
@@ -763,97 +463,6 @@ class TwoQubitControlledUDecomposer:
763
463
  return circ
764
464
 
765
465
 
766
- class TwoQubitWeylMirrorControlledEquiv(TwoQubitWeylDecomposition):
767
- r""":math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot \text{Ctrl-U}`
768
-
769
- This gate binds 4 parameters, we make it canonical by setting:
770
- :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)`.
771
- """
772
-
773
- def specialize(self):
774
- self.a = self.b = np.pi / 4
775
- k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_zyz.angles_and_phase(self.K2l)
776
- k2rtheta, k2rphi, k2rlambda, k2rphase = _oneq_zyz.angles_and_phase(self.K2r)
777
- self.global_phase += k2lphase + k2rphase
778
- self.K1r = self.K1r @ np.asarray(RZGate(k2lphi))
779
- self.K1l = self.K1l @ np.asarray(RZGate(k2rphi))
780
- self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RZGate(k2llambda))
781
- self.K2r = np.asarray(RYGate(k2rtheta)) @ np.asarray(RZGate(k2rlambda))
782
-
783
- def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
784
- circ.swap(0, 1)
785
- circ.rzz((np.pi / 4 - self.c) * 2, 0, 1)
786
- circ.global_phase += np.pi / 4
787
-
788
-
789
- # These next 3 gates use the definition of fSim from https://arxiv.org/pdf/2001.08343.pdf eq (1)
790
- class TwoQubitWeylfSimaabEquiv(TwoQubitWeylDecomposition):
791
- r""":math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|`
792
-
793
- This gate binds 5 parameters, we make it canonical by setting:
794
- :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`.
795
- """
796
-
797
- def specialize(self):
798
- self.a = self.b = (self.a + self.b) / 2
799
- k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_zyz.angles_and_phase(self.K2l)
800
- self.global_phase += k2lphase
801
- self.K1r = self.K1r @ np.asarray(RZGate(k2lphi))
802
- self.K1l = self.K1l @ np.asarray(RZGate(k2lphi))
803
- self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RZGate(k2llambda))
804
- self.K2r = np.asarray(RZGate(-k2lphi)) @ self.K2r
805
-
806
-
807
- class TwoQubitWeylfSimabbEquiv(TwoQubitWeylDecomposition):
808
- r""":math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0`
809
-
810
- This gate binds 5 parameters, we make it canonical by setting:
811
- :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)`.
812
- """
813
-
814
- _default_1q_basis = "XYX"
815
-
816
- def specialize(self):
817
- self.b = self.c = (self.b + self.c) / 2
818
- k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
819
- self.global_phase += k2lphase
820
- self.K1r = self.K1r @ np.asarray(RXGate(k2lphi))
821
- self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
822
- self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
823
- self.K2r = np.asarray(RXGate(-k2lphi)) @ self.K2r
824
-
825
-
826
- class TwoQubitWeylfSimabmbEquiv(TwoQubitWeylDecomposition):
827
- r""":math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0`
828
-
829
- This gate binds 5 parameters, we make it canonical by setting:
830
- :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)`.
831
- """
832
-
833
- _default_1q_basis = "XYX"
834
-
835
- def specialize(self):
836
- self.b = (self.b - self.c) / 2
837
- self.c = -self.b
838
- k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
839
- self.global_phase += k2lphase
840
- self.K1r = self.K1r @ _ipz @ np.asarray(RXGate(k2lphi)) @ _ipz
841
- self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
842
- self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
843
- self.K2r = _ipz @ np.asarray(RXGate(-k2lphi)) @ _ipz @ self.K2r
844
-
845
-
846
- class TwoQubitWeylGeneral(TwoQubitWeylDecomposition):
847
- """U has no special symmetry.
848
-
849
- This gate binds all 6 possible parameters, so there is no need to make the single-qubit
850
- pre-/post-gates canonical.
851
- """
852
-
853
- def specialize(self):
854
- pass # Nothing to do
855
-
856
-
857
466
  def Ud(a, b, c):
858
467
  r"""Generates the array :math:`e^{(i a XX + i b YY + i c ZZ)}`"""
859
468
  return np.array(
@@ -904,6 +513,7 @@ class TwoQubitBasisDecomposer:
904
513
  If ``False``, don't attempt optimization. If ``None``, attempt optimization but don't raise
905
514
  if unknown.
906
515
 
516
+
907
517
  .. automethod:: __call__
908
518
  """
909
519
 
@@ -917,152 +527,34 @@ class TwoQubitBasisDecomposer:
917
527
  self.gate = gate
918
528
  self.basis_fidelity = basis_fidelity
919
529
  self.pulse_optimize = pulse_optimize
920
-
921
- basis = self.basis = TwoQubitWeylDecomposition(Operator(gate).data)
922
- self._decomposer1q = OneQubitEulerDecomposer(euler_basis)
923
-
924
- # FIXME: find good tolerances
925
- self.is_supercontrolled = math.isclose(basis.a, np.pi / 4) and math.isclose(basis.c, 0.0)
926
-
927
- # Create some useful matrices U1, U2, U3 are equivalent to the basis,
928
- # expand as Ui = Ki1.Ubasis.Ki2
929
- b = basis.b
930
- K11l = (
931
- 1
932
- / (1 + 1j)
933
- * np.array(
934
- [
935
- [-1j * cmath.exp(-1j * b), cmath.exp(-1j * b)],
936
- [-1j * cmath.exp(1j * b), -cmath.exp(1j * b)],
937
- ],
938
- dtype=complex,
939
- )
940
- )
941
- K11r = (
942
- 1
943
- / math.sqrt(2)
944
- * np.array(
945
- [
946
- [1j * cmath.exp(-1j * b), -cmath.exp(-1j * b)],
947
- [cmath.exp(1j * b), -1j * cmath.exp(1j * b)],
948
- ],
949
- dtype=complex,
950
- )
951
- )
952
- K12l = 1 / (1 + 1j) * np.array([[1j, 1j], [-1, 1]], dtype=complex)
953
- K12r = 1 / math.sqrt(2) * np.array([[1j, 1], [-1, -1j]], dtype=complex)
954
- K32lK21l = (
955
- 1
956
- / math.sqrt(2)
957
- * np.array(
958
- [
959
- [1 + 1j * np.cos(2 * b), 1j * np.sin(2 * b)],
960
- [1j * np.sin(2 * b), 1 - 1j * np.cos(2 * b)],
961
- ],
962
- dtype=complex,
963
- )
964
- )
965
- K21r = (
966
- 1
967
- / (1 - 1j)
968
- * np.array(
969
- [
970
- [-1j * cmath.exp(-2j * b), cmath.exp(-2j * b)],
971
- [1j * cmath.exp(2j * b), cmath.exp(2j * b)],
972
- ],
973
- dtype=complex,
974
- )
975
- )
976
- K22l = 1 / math.sqrt(2) * np.array([[1, -1], [1, 1]], dtype=complex)
977
- K22r = np.array([[0, 1], [-1, 0]], dtype=complex)
978
- K31l = (
979
- 1
980
- / math.sqrt(2)
981
- * np.array(
982
- [[cmath.exp(-1j * b), cmath.exp(-1j * b)], [-cmath.exp(1j * b), cmath.exp(1j * b)]],
983
- dtype=complex,
984
- )
985
- )
986
- K31r = 1j * np.array([[cmath.exp(1j * b), 0], [0, -cmath.exp(-1j * b)]], dtype=complex)
987
- K32r = (
988
- 1
989
- / (1 - 1j)
990
- * np.array(
991
- [
992
- [cmath.exp(1j * b), -cmath.exp(-1j * b)],
993
- [-1j * cmath.exp(1j * b), -1j * cmath.exp(-1j * b)],
994
- ],
995
- dtype=complex,
996
- )
530
+ # Use cx as gate name for pulse optimal decomposition detection
531
+ # otherwise use USER_GATE as a unique key to support custom gates
532
+ # including parameterized gates like UnitaryGate.
533
+ if isinstance(gate, CXGate):
534
+ gate_name = "cx"
535
+ else:
536
+ gate_name = "USER_GATE"
537
+
538
+ self._inner_decomposer = two_qubit_decompose.TwoQubitBasisDecomposer(
539
+ gate_name,
540
+ Operator(gate).data,
541
+ basis_fidelity=basis_fidelity,
542
+ euler_basis=euler_basis,
543
+ pulse_optimize=pulse_optimize,
997
544
  )
998
- k1ld = basis.K1l.T.conj()
999
- k1rd = basis.K1r.T.conj()
1000
- k2ld = basis.K2l.T.conj()
1001
- k2rd = basis.K2r.T.conj()
1002
-
1003
- # Pre-build the fixed parts of the matrices used in 3-part decomposition
1004
- self.u0l = K31l.dot(k1ld)
1005
- self.u0r = K31r.dot(k1rd)
1006
- self.u1l = k2ld.dot(K32lK21l).dot(k1ld)
1007
- self.u1ra = k2rd.dot(K32r)
1008
- self.u1rb = K21r.dot(k1rd)
1009
- self.u2la = k2ld.dot(K22l)
1010
- self.u2lb = K11l.dot(k1ld)
1011
- self.u2ra = k2rd.dot(K22r)
1012
- self.u2rb = K11r.dot(k1rd)
1013
- self.u3l = k2ld.dot(K12l)
1014
- self.u3r = k2rd.dot(K12r)
1015
-
1016
- # Pre-build the fixed parts of the matrices used in the 2-part decomposition
1017
- self.q0l = K12l.T.conj().dot(k1ld)
1018
- self.q0r = K12r.T.conj().dot(_ipz).dot(k1rd)
1019
- self.q1la = k2ld.dot(K11l.T.conj())
1020
- self.q1lb = K11l.dot(k1ld)
1021
- self.q1ra = k2rd.dot(_ipz).dot(K11r.T.conj())
1022
- self.q1rb = K11r.dot(k1rd)
1023
- self.q2l = k2ld.dot(K12l)
1024
- self.q2r = k2rd.dot(K12r)
1025
-
1026
- # Decomposition into different number of gates
1027
- # In the future could use different decomposition functions for different basis classes, etc
545
+ self.is_supercontrolled = self._inner_decomposer.super_controlled
1028
546
  if not self.is_supercontrolled:
1029
547
  warnings.warn(
1030
- "Only know how to decompose properly for supercontrolled basis gate. "
1031
- "This gate is ~Ud({}, {}, {})".format(basis.a, basis.b, basis.c),
548
+ "Only know how to decompose properly for a supercontrolled basis gate.",
1032
549
  stacklevel=2,
1033
550
  )
1034
- self.decomposition_fns = [
1035
- self.decomp0,
1036
- self.decomp1,
1037
- self.decomp2_supercontrolled,
1038
- self.decomp3_supercontrolled,
1039
- ]
1040
- self._rqc = None
1041
551
 
1042
- def traces(self, target):
1043
- r"""
1044
- Give the expected traces :math:`\Big\vert\text{Tr}(U \cdot U_\text{target}^{\dag})\Big\vert`
1045
- for a different number of basis gates.
552
+ def num_basis_gates(self, unitary):
553
+ """Computes the number of basis gates needed in
554
+ a decomposition of input unitary
1046
555
  """
1047
- # Future gotcha: extending this to non-supercontrolled basis.
1048
- # Careful: closest distance between a1,b1,c1 and a2,b2,c2 may be between reflections.
1049
- # This doesn't come up if either c1==0 or c2==0 but otherwise be careful.
1050
- ta, tb, tc = target.a, target.b, target.c
1051
- bb = self.basis.b
1052
- return [
1053
- 4
1054
- * complex(
1055
- math.cos(ta) * math.cos(tb) * math.cos(tc),
1056
- math.sin(ta) * math.sin(tb) * math.sin(tc),
1057
- ),
1058
- 4
1059
- * complex(
1060
- math.cos(math.pi / 4 - ta) * math.cos(bb - tb) * math.cos(tc),
1061
- math.sin(math.pi / 4 - ta) * math.sin(bb - tb) * math.sin(tc),
1062
- ),
1063
- 4 * math.cos(tc),
1064
- 4,
1065
- ]
556
+ unitary = np.asarray(unitary, dtype=complex)
557
+ return self._inner_decomposer.num_basis_gates(unitary)
1066
558
 
1067
559
  @staticmethod
1068
560
  def decomp0(target):
@@ -1078,9 +570,7 @@ class TwoQubitBasisDecomposer:
1078
570
  which is optimal for all targets and bases
1079
571
  """
1080
572
 
1081
- U0l = target.K1l.dot(target.K2l)
1082
- U0r = target.K1r.dot(target.K2r)
1083
- return U0r, U0l
573
+ return two_qubit_decompose.TwoQubitBasisDecomposer.decomp0(target)
1084
574
 
1085
575
  def decomp1(self, target):
1086
576
  r"""Decompose target :math:`\sim U_d(x, y, z)` with :math:`1` use of the basis gate
@@ -1094,13 +584,7 @@ class TwoQubitBasisDecomposer:
1094
584
 
1095
585
  which is optimal for all targets and bases with ``z==0`` or ``c==0``.
1096
586
  """
1097
- # FIXME: fix for z!=0 and c!=0 using closest reflection (not always in the Weyl chamber)
1098
- U0l = target.K1l.dot(self.basis.K1l.T.conj())
1099
- U0r = target.K1r.dot(self.basis.K1r.T.conj())
1100
- U1l = self.basis.K2l.T.conj().dot(target.K2l)
1101
- U1r = self.basis.K2r.T.conj().dot(target.K2r)
1102
-
1103
- return U1r, U1l, U0r, U0l
587
+ return self._inner_decomposer.decomp1(target)
1104
588
 
1105
589
  def decomp2_supercontrolled(self, target):
1106
590
  r"""
@@ -1119,15 +603,7 @@ class TwoQubitBasisDecomposer:
1119
603
  This is an exact decomposition for supercontrolled basis and target :math:`\sim U_d(x, y, 0)`.
1120
604
  No guarantees for non-supercontrolled basis.
1121
605
  """
1122
-
1123
- U0l = target.K1l.dot(self.q0l)
1124
- U0r = target.K1r.dot(self.q0r)
1125
- U1l = self.q1la.dot(rz_array(-2 * target.a)).dot(self.q1lb)
1126
- U1r = self.q1ra.dot(rz_array(2 * target.b)).dot(self.q1rb)
1127
- U2l = self.q2l.dot(target.K2l)
1128
- U2r = self.q2r.dot(target.K2r)
1129
-
1130
- return U2r, U2l, U1r, U1l, U0r, U0l
606
+ return self._inner_decomposer.decomp2_supercontrolled(target)
1131
607
 
1132
608
  def decomp3_supercontrolled(self, target):
1133
609
  r"""
@@ -1135,26 +611,17 @@ class TwoQubitBasisDecomposer:
1135
611
  This is an exact decomposition for supercontrolled basis :math:`\sim U_d(\pi/4, b, 0)`, all b,
1136
612
  and any target. No guarantees for non-supercontrolled basis.
1137
613
  """
1138
-
1139
- U0l = target.K1l.dot(self.u0l)
1140
- U0r = target.K1r.dot(self.u0r)
1141
- U1l = self.u1l
1142
- U1r = self.u1ra.dot(rz_array(-2 * target.c)).dot(self.u1rb)
1143
- U2l = self.u2la.dot(rz_array(-2 * target.a)).dot(self.u2lb)
1144
- U2r = self.u2ra.dot(rz_array(2 * target.b)).dot(self.u2rb)
1145
- U3l = self.u3l.dot(target.K2l)
1146
- U3r = self.u3r.dot(target.K2r)
1147
-
1148
- return U3r, U3l, U2r, U2l, U1r, U1l, U0r, U0l
614
+ return self._inner_decomposer.decomp3_supercontrolled(target)
1149
615
 
1150
616
  def __call__(
1151
617
  self,
1152
618
  unitary: Operator | np.ndarray,
1153
619
  basis_fidelity: float | None = None,
1154
620
  approximate: bool = True,
621
+ use_dag: bool = False,
1155
622
  *,
1156
623
  _num_basis_uses: int | None = None,
1157
- ) -> QuantumCircuit:
624
+ ) -> QuantumCircuit | DAGCircuit:
1158
625
  r"""Decompose a two-qubit ``unitary`` over fixed basis and :math:`SU(2)` using the best
1159
626
  approximation given that each basis application has a finite ``basis_fidelity``.
1160
627
 
@@ -1163,6 +630,8 @@ class TwoQubitBasisDecomposer:
1163
630
  basis_fidelity (float or None): Fidelity to be assumed for applications of KAK Gate.
1164
631
  If given, overrides ``basis_fidelity`` given at init.
1165
632
  approximate (bool): Approximates if basis fidelities are less than 1.0.
633
+ use_dag (bool): If true a :class:`.DAGCircuit` is returned instead of a
634
+ :class:`QuantumCircuit` when this class is called.
1166
635
  _num_basis_uses (int): force a particular approximation by passing a number in [0, 3].
1167
636
 
1168
637
  Returns:
@@ -1171,313 +640,55 @@ class TwoQubitBasisDecomposer:
1171
640
  Raises:
1172
641
  QiskitError: if ``pulse_optimize`` is True but we don't know how to do it.
1173
642
  """
1174
- basis_fidelity = basis_fidelity or self.basis_fidelity
1175
- if approximate is False:
1176
- basis_fidelity = 1.0
1177
- unitary = np.asarray(unitary, dtype=complex)
1178
643
 
1179
- target_decomposed = TwoQubitWeylDecomposition(unitary)
1180
- traces = self.traces(target_decomposed)
1181
- expected_fidelities = [trace_to_fid(traces[i]) * basis_fidelity**i for i in range(4)]
1182
-
1183
- best_nbasis = int(np.argmax(expected_fidelities))
1184
- if _num_basis_uses is not None:
1185
- best_nbasis = _num_basis_uses
1186
- decomposition = self.decomposition_fns[best_nbasis](target_decomposed)
1187
-
1188
- # attempt pulse optimal decomposition
1189
- try:
1190
- if self.pulse_optimize in {None, True}:
1191
- return_circuit = self._pulse_optimal_chooser(
1192
- best_nbasis, decomposition, target_decomposed
1193
- )
1194
- if return_circuit:
1195
- return return_circuit
1196
- except QiskitError:
1197
- if self.pulse_optimize:
1198
- raise
1199
-
1200
- # do default decomposition
644
+ sequence = self._inner_decomposer(
645
+ np.asarray(unitary, dtype=complex),
646
+ basis_fidelity,
647
+ approximate,
648
+ _num_basis_uses=_num_basis_uses,
649
+ )
1201
650
  q = QuantumRegister(2)
1202
- decomposition_euler = [self._decomposer1q._decompose(x) for x in decomposition]
1203
- return_circuit = QuantumCircuit(q)
1204
- return_circuit.global_phase = target_decomposed.global_phase
1205
- return_circuit.global_phase -= best_nbasis * self.basis.global_phase
1206
- if best_nbasis == 2:
1207
- return_circuit.global_phase += np.pi
1208
- for i in range(best_nbasis):
1209
- return_circuit.compose(decomposition_euler[2 * i], [q[0]], inplace=True)
1210
- return_circuit.compose(decomposition_euler[2 * i + 1], [q[1]], inplace=True)
1211
- return_circuit.append(self.gate, [q[0], q[1]])
1212
- return_circuit.compose(decomposition_euler[2 * best_nbasis], [q[0]], inplace=True)
1213
- return_circuit.compose(decomposition_euler[2 * best_nbasis + 1], [q[1]], inplace=True)
1214
- return return_circuit
1215
-
1216
- def _pulse_optimal_chooser(
1217
- self, best_nbasis, decomposition, target_decomposed
1218
- ) -> QuantumCircuit:
1219
- """Determine method to find pulse optimal circuit. This method may be
1220
- removed once a more general approach is used.
1221
-
1222
- Returns:
1223
- QuantumCircuit: pulse optimal quantum circuit.
1224
- None: Probably ``nbasis==1`` and original circuit is fine.
1225
-
1226
- Raises:
1227
- QiskitError: Decomposition for selected basis not implemented.
1228
- """
1229
- circuit = None
1230
- if self.pulse_optimize and best_nbasis in {0, 1}:
1231
- # already pulse optimal
1232
- return None
1233
- elif self.pulse_optimize and best_nbasis > 3:
1234
- raise QiskitError(
1235
- f"Unexpected number of entangling gates ({best_nbasis}) in decomposition."
1236
- )
1237
- if self._decomposer1q.basis in {"ZSX", "ZSXX"}:
1238
- if isinstance(self.gate, CXGate):
1239
- if best_nbasis == 3:
1240
- circuit = self._get_sx_vz_3cx_efficient_euler(decomposition, target_decomposed)
1241
- elif best_nbasis == 2:
1242
- circuit = self._get_sx_vz_2cx_efficient_euler(decomposition, target_decomposed)
1243
- else:
1244
- raise QiskitError("pulse_optimizer currently only works with CNOT entangling gate")
651
+ if use_dag:
652
+ from qiskit.dagcircuit.dagcircuit import DAGCircuit
653
+
654
+ dag = DAGCircuit()
655
+ dag.global_phase = sequence.global_phase
656
+ dag.add_qreg(q)
657
+ for name, params, qubits in sequence:
658
+ if name == "USER_GATE":
659
+ dag.apply_operation_back(self.gate, tuple(q[x] for x in qubits), check=False)
660
+ else:
661
+ gate = GATE_NAME_MAP[name](*params)
662
+ dag.apply_operation_back(gate, tuple(q[x] for x in qubits), check=False)
663
+ return dag
1245
664
  else:
1246
- raise QiskitError(
1247
- '"pulse_optimize" currently only works with ZSX basis '
1248
- f"({self._decomposer1q.basis} used)"
1249
- )
1250
- return circuit
665
+ circ = QuantumCircuit(q, global_phase=sequence.global_phase)
666
+ for name, params, qubits in sequence:
667
+ try:
668
+ getattr(circ, name)(*params, *qubits)
669
+ except AttributeError as exc:
670
+ if name == "USER_GATE":
671
+ circ.append(self.gate, qubits)
672
+ elif name == "u3":
673
+ gate = U3Gate(*params)
674
+ circ.append(gate, qubits)
675
+ elif name == "u2":
676
+ gate = U2Gate(*params)
677
+ circ.append(gate, qubits)
678
+ elif name == "u1":
679
+ gate = U1Gate(*params)
680
+ circ.append(gate, qubits)
681
+ else:
682
+ raise QiskitError(f"Unknown gate {name}") from exc
683
+
684
+ return circ
1251
685
 
1252
- def _get_sx_vz_2cx_efficient_euler(self, decomposition, target_decomposed):
1253
- """
1254
- Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming
1255
- two CNOT gates are needed.
1256
-
1257
- This first decomposes each unitary from the KAK decomposition into ZXZ on the source
1258
- qubit of the CNOTs and XZX on the targets in order to commute operators to beginning and
1259
- end of decomposition. The beginning and ending single qubit gates are then
1260
- collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
1261
- if performance is a concern.
1262
- """
1263
- best_nbasis = 2 # by assumption
1264
- num_1q_uni = len(decomposition)
1265
- # list of euler angle decompositions on qubits 0 and 1
1266
- euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
1267
- euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
1268
- global_phase = 0.0
1269
-
1270
- # decompose source unitaries to zxz
1271
- zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
1272
- for iqubit, decomp in enumerate(decomposition[0::2]):
1273
- euler_angles = zxz_decomposer.angles_and_phase(decomp)
1274
- euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
1275
- global_phase += euler_angles[3]
1276
- # decompose target unitaries to xzx
1277
- xzx_decomposer = OneQubitEulerDecomposer("XZX")
1278
- for iqubit, decomp in enumerate(decomposition[1::2]):
1279
- euler_angles = xzx_decomposer.angles_and_phase(decomp)
1280
- euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
1281
- global_phase += euler_angles[3]
1282
- qc = QuantumCircuit(2)
1283
- qc.global_phase = target_decomposed.global_phase
1284
- qc.global_phase -= best_nbasis * self.basis.global_phase
1285
- qc.global_phase += global_phase
1286
-
1287
- # TODO: make this more effecient to avoid double decomposition
1288
- # prepare beginning 0th qubit local unitary
1289
- circ = QuantumCircuit(1)
1290
- circ.rz(euler_q0[0][0], 0)
1291
- circ.rx(euler_q0[0][1], 0)
1292
- circ.rz(euler_q0[0][2] + euler_q0[1][0] + math.pi / 2, 0)
1293
- # re-decompose to basis of 1q decomposer
1294
- qceuler = self._decomposer1q(Operator(circ).data)
1295
- qc.compose(qceuler, [0], inplace=True)
1296
-
1297
- # prepare beginning 1st qubit local unitary
1298
- circ = QuantumCircuit(1)
1299
- circ.rx(euler_q1[0][0], 0)
1300
- circ.rz(euler_q1[0][1], 0)
1301
- circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
1302
- qceuler = self._decomposer1q(Operator(circ).data)
1303
- qc.compose(qceuler, [1], inplace=True)
1304
-
1305
- qc.cx(0, 1)
1306
- # the central decompositions are dependent on the specific form of the
1307
- # unitaries coming out of the two qubit decomposer which have some flexibility
1308
- # of choice.
1309
- qc.sx(0)
1310
- qc.rz(euler_q0[1][1] - math.pi, 0)
1311
- qc.sx(0)
1312
- qc.rz(euler_q1[1][1], 1)
1313
- qc.global_phase += math.pi / 2
1314
-
1315
- qc.cx(0, 1)
1316
-
1317
- circ = QuantumCircuit(1)
1318
- circ.rz(euler_q0[1][2] + euler_q0[2][0] + math.pi / 2, 0)
1319
- circ.rx(euler_q0[2][1], 0)
1320
- circ.rz(euler_q0[2][2], 0)
1321
- qceuler = self._decomposer1q(Operator(circ).data)
1322
- qc.compose(qceuler, [0], inplace=True)
1323
- circ = QuantumCircuit(1)
1324
- circ.rx(euler_q1[1][2] + euler_q1[2][0], 0)
1325
- circ.rz(euler_q1[2][1], 0)
1326
- circ.rx(euler_q1[2][2], 0)
1327
- qceuler = self._decomposer1q(Operator(circ).data)
1328
- qc.compose(qceuler, [1], inplace=True)
1329
-
1330
- return qc
1331
-
1332
- def _get_sx_vz_3cx_efficient_euler(self, decomposition, target_decomposed):
1333
- """
1334
- Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming
1335
- three CNOT gates are needed.
1336
-
1337
- This first decomposes each unitary from the KAK decomposition into ZXZ on the source
1338
- qubit of the CNOTs and XZX on the targets in order commute operators to beginning and
1339
- end of decomposition. Inserting Hadamards reverses the direction of the CNOTs and transforms
1340
- a variable Rx -> variable virtual Rz. The beginning and ending single qubit gates are then
1341
- collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
1342
- if performance is a concern.
1343
- """
1344
- best_nbasis = 3 # by assumption
1345
- num_1q_uni = len(decomposition)
1346
- # create structure to hold euler angles: 1st index represents unitary "group" wrt cx
1347
- # 2nd index represents index of euler triple.
1348
- euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
1349
- euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
1350
- global_phase = 0.0
1351
- atol = 1e-10 # absolute tolerance for floats
1352
-
1353
- # decompose source unitaries to zxz
1354
- zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
1355
- for iqubit, decomp in enumerate(decomposition[0::2]):
1356
- euler_angles = zxz_decomposer.angles_and_phase(decomp)
1357
- euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
1358
- global_phase += euler_angles[3]
1359
- # decompose target unitaries to xzx
1360
- xzx_decomposer = OneQubitEulerDecomposer("XZX")
1361
- for iqubit, decomp in enumerate(decomposition[1::2]):
1362
- euler_angles = xzx_decomposer.angles_and_phase(decomp)
1363
- euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
1364
- global_phase += euler_angles[3]
1365
-
1366
- qc = QuantumCircuit(2)
1367
- qc.global_phase = target_decomposed.global_phase
1368
- qc.global_phase -= best_nbasis * self.basis.global_phase
1369
- qc.global_phase += global_phase
1370
-
1371
- x12 = euler_q0[1][2] + euler_q0[2][0]
1372
- x12_isNonZero = not math.isclose(x12, 0, abs_tol=atol)
1373
- x12_isOddMult = None
1374
- x12_isPiMult = math.isclose(math.sin(x12), 0, abs_tol=atol)
1375
- if x12_isPiMult:
1376
- x12_isOddMult = math.isclose(math.cos(x12), -1, abs_tol=atol)
1377
- x12_phase = math.pi * math.cos(x12)
1378
- x02_add = x12 - euler_q0[1][0]
1379
- x12_isHalfPi = math.isclose(x12, math.pi / 2, abs_tol=atol)
1380
-
1381
- # TODO: make this more effecient to avoid double decomposition
1382
- circ = QuantumCircuit(1)
1383
- circ.rz(euler_q0[0][0], 0)
1384
- circ.rx(euler_q0[0][1], 0)
1385
- if x12_isNonZero and x12_isPiMult:
1386
- circ.rz(euler_q0[0][2] - x02_add, 0)
1387
- else:
1388
- circ.rz(euler_q0[0][2] + euler_q0[1][0], 0)
1389
- circ.h(0)
1390
- qceuler = self._decomposer1q(Operator(circ).data)
1391
- qc.compose(qceuler, [0], inplace=True)
1392
-
1393
- circ = QuantumCircuit(1)
1394
- circ.rx(euler_q1[0][0], 0)
1395
- circ.rz(euler_q1[0][1], 0)
1396
- circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
1397
- circ.h(0)
1398
- qceuler = self._decomposer1q(Operator(circ).data)
1399
- qc.compose(qceuler, [1], inplace=True)
1400
-
1401
- qc.cx(1, 0)
1402
-
1403
- if x12_isPiMult:
1404
- # even or odd multiple
1405
- if x12_isNonZero:
1406
- qc.global_phase += x12_phase
1407
- if x12_isNonZero and x12_isOddMult:
1408
- qc.rz(-euler_q0[1][1], 0)
1409
- else:
1410
- qc.rz(euler_q0[1][1], 0)
1411
- qc.global_phase += math.pi
1412
- if x12_isHalfPi:
1413
- qc.sx(0)
1414
- qc.global_phase -= math.pi / 4
1415
- elif x12_isNonZero and not x12_isPiMult:
1416
- # this is non-optimal but doesn't seem to occur currently
1417
- if self.pulse_optimize is None:
1418
- qc.compose(self._decomposer1q(Operator(RXGate(x12)).data), [0], inplace=True)
1419
- else:
1420
- raise QiskitError("possible non-pulse-optimal decomposition encountered")
1421
- if math.isclose(euler_q1[1][1], math.pi / 2, abs_tol=atol):
1422
- qc.sx(1)
1423
- qc.global_phase -= math.pi / 4
1424
- else:
1425
- # this is non-optimal but doesn't seem to occur currently
1426
- if self.pulse_optimize is None:
1427
- qc.compose(
1428
- self._decomposer1q(Operator(RXGate(euler_q1[1][1])).data), [1], inplace=True
1429
- )
1430
- else:
1431
- raise QiskitError("possible non-pulse-optimal decomposition encountered")
1432
- qc.rz(euler_q1[1][2] + euler_q1[2][0], 1)
1433
-
1434
- qc.cx(1, 0)
1435
-
1436
- qc.rz(euler_q0[2][1], 0)
1437
- if math.isclose(euler_q1[2][1], math.pi / 2, abs_tol=atol):
1438
- qc.sx(1)
1439
- qc.global_phase -= math.pi / 4
1440
- else:
1441
- # this is non-optimal but doesn't seem to occur currently
1442
- if self.pulse_optimize is None:
1443
- qc.compose(
1444
- self._decomposer1q(Operator(RXGate(euler_q1[2][1])).data), [1], inplace=True
1445
- )
1446
- else:
1447
- raise QiskitError("possible non-pulse-optimal decomposition encountered")
1448
-
1449
- qc.cx(1, 0)
1450
-
1451
- circ = QuantumCircuit(1)
1452
- circ.h(0)
1453
- circ.rz(euler_q0[2][2] + euler_q0[3][0], 0)
1454
- circ.rx(euler_q0[3][1], 0)
1455
- circ.rz(euler_q0[3][2], 0)
1456
- qceuler = self._decomposer1q(Operator(circ).data)
1457
- qc.compose(qceuler, [0], inplace=True)
1458
-
1459
- circ = QuantumCircuit(1)
1460
- circ.h(0)
1461
- circ.rx(euler_q1[2][2] + euler_q1[3][0], 0)
1462
- circ.rz(euler_q1[3][1], 0)
1463
- circ.rx(euler_q1[3][2], 0)
1464
- qceuler = self._decomposer1q(Operator(circ).data)
1465
- qc.compose(qceuler, [1], inplace=True)
1466
-
1467
- # TODO: fix the sign problem to avoid correction here
1468
- if cmath.isclose(
1469
- target_decomposed.unitary_matrix[0, 0], -(Operator(qc).data[0, 0]), abs_tol=atol
1470
- ):
1471
- qc.global_phase += math.pi
1472
- return qc
1473
-
1474
- def num_basis_gates(self, unitary):
1475
- """Computes the number of basis gates needed in
1476
- a decomposition of input unitary
686
+ def traces(self, target):
687
+ r"""
688
+ Give the expected traces :math:`\Big\vert\text{Tr}(U \cdot U_\text{target}^{\dag})\Big\vert`
689
+ for a different number of basis gates.
1477
690
  """
1478
- return two_qubit_decompose._num_basis_gates(
1479
- self.basis.b, self.basis_fidelity, np.asarray(unitary, dtype=complex)
1480
- )
691
+ return self._inner_decomposer.traces(target._inner_decomposition)
1481
692
 
1482
693
 
1483
694
  class TwoQubitDecomposeUpToDiagonal: