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
@@ -1,421 +0,0 @@
1
- # This code is part of Qiskit.
2
- #
3
- # (C) Copyright IBM 2017, 2024.
4
- #
5
- # This code is licensed under the Apache License, Version 2.0. You may
6
- # obtain a copy of this license in the LICENSE.txt file in the root directory
7
- # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
- #
9
- # Any modifications or derivative works of this code must retain this
10
- # copyright notice, and modified files need to carry a notice indicating
11
- # that they have been altered from the originals.
12
-
13
- """Algebra utilities and the ``GateSequence`` class."""
14
-
15
- from __future__ import annotations
16
-
17
- from collections.abc import Sequence
18
- import math
19
- import numpy as np
20
-
21
- from qiskit.circuit import Gate, QuantumCircuit, Qubit
22
- from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate
23
-
24
-
25
- class GateSequence:
26
- """A class implementing a sequence of gates.
27
-
28
- This class stores the sequence of gates along with the unitary they implement.
29
- """
30
-
31
- def __init__(self, gates: Sequence[Gate] = ()) -> None:
32
- """Create a new sequence of gates.
33
-
34
- Args:
35
- gates: The gates in the sequence. The default is [].
36
- """
37
- self.gates = list(gates)
38
- self.matrices = [np.asarray(gate, dtype=np.complex128) for gate in gates]
39
- self.labels = [gate.name for gate in gates]
40
-
41
- # get U(2) representation of the gate sequence
42
- u2_matrix = np.identity(2)
43
- for matrix in self.matrices:
44
- # idea: could this be optimized by a specific numpy operation?
45
- u2_matrix = matrix.dot(u2_matrix)
46
-
47
- # convert to SU(2)
48
- su2_matrix, global_phase = _convert_u2_to_su2(u2_matrix)
49
-
50
- # convert to SO(3), that's what the Solovay Kitaev algorithm uses
51
- so3_matrix = _convert_su2_to_so3(su2_matrix)
52
-
53
- # store the matrix and the global phase
54
- self._eulers = None
55
- self.name = " ".join(self.labels)
56
- self.global_phase = global_phase
57
- self.product = so3_matrix
58
-
59
- def remove_cancelling_pair(self, indices: Sequence[int]) -> None:
60
- """Remove a pair of indices that cancel each other and *do not* change the matrices."""
61
- for index in list(indices[::-1]):
62
- self.gates.pop(index)
63
- self.labels.pop(index)
64
-
65
- # restore name
66
- self.name = " ".join(self.labels)
67
-
68
- def __eq__(self, other: "GateSequence") -> bool:
69
- """Check if this GateSequence is the same as the other GateSequence.
70
-
71
- Args:
72
- other: The GateSequence that will be compared to ``self``.
73
-
74
- Returns:
75
- True if ``other`` is equivalent to ``self``, false otherwise.
76
-
77
- """
78
- if not len(self.gates) == len(other.gates):
79
- return False
80
-
81
- for gate1, gate2 in zip(self.gates, other.gates):
82
- if gate1 != gate2:
83
- return False
84
-
85
- if self.global_phase != other.global_phase:
86
- return False
87
-
88
- return True
89
-
90
- def to_circuit(self):
91
- """Convert to a circuit.
92
-
93
- If no gates set but the product is not the identity, returns a circuit with a
94
- unitary operation to implement the matrix.
95
- """
96
- if len(self.gates) == 0 and not np.allclose(self.product, np.identity(3)):
97
- circuit = QuantumCircuit(1, global_phase=self.global_phase)
98
- su2 = _convert_so3_to_su2(self.product)
99
- circuit.unitary(su2, [0])
100
- return circuit
101
-
102
- circuit = QuantumCircuit(1, global_phase=self.global_phase)
103
- for gate in self.gates:
104
- circuit.append(gate, [0])
105
-
106
- return circuit
107
-
108
- def _to_u2(self):
109
- """Creates the U2 matrix corresponding to the stored sequence of gates
110
- and the global phase.
111
- """
112
- u2 = np.eye(2, dtype=complex)
113
- for mat in self.gates:
114
- u2 = mat.to_matrix().dot(u2)
115
- u2 = np.exp(1j * self.global_phase) * u2
116
- return u2
117
-
118
- def to_dag(self):
119
- """Convert to a :class:`.DAGCircuit`.
120
-
121
- If no gates set but the product is not the identity, returns a circuit with a
122
- unitary operation to implement the matrix.
123
- """
124
- from qiskit.dagcircuit import DAGCircuit
125
-
126
- qreg = (Qubit(),)
127
- dag = DAGCircuit()
128
- dag.add_qubits(qreg)
129
-
130
- if len(self.gates) == 0 and not np.allclose(self.product, np.identity(3)):
131
- su2 = _convert_so3_to_su2(self.product)
132
- dag.apply_operation_back(UnitaryGate(su2), qreg, check=False)
133
- return dag
134
-
135
- dag.global_phase = self.global_phase
136
- for gate in self.gates:
137
- dag.apply_operation_back(gate, qreg, check=False)
138
-
139
- return dag
140
-
141
- def append(self, gate: Gate) -> "GateSequence":
142
- """Append gate to the sequence of gates.
143
-
144
- Args:
145
- gate: The gate to be appended.
146
-
147
- Returns:
148
- GateSequence with ``gate`` appended.
149
- """
150
- # invalidate euler angles and name
151
- self._eulers = None
152
-
153
- # TODO: this recomputes the product whenever we append something, which could be more
154
- # efficient by storing the current matrix and just multiplying the input gate to it
155
- # self.product = convert_su2_to_so3(self._compute_product(self.gates))
156
- matrix = np.array(gate, dtype=np.complex128)
157
- su2, phase = _convert_u2_to_su2(matrix)
158
- so3 = _convert_su2_to_so3(su2)
159
-
160
- self.product = so3.dot(self.product)
161
- self.global_phase = self.global_phase + phase
162
-
163
- self.gates.append(gate)
164
- if len(self.labels) > 0:
165
- self.name += f" {gate.name}"
166
- else:
167
- self.name = gate.name
168
- self.labels.append(gate.name)
169
-
170
- self.matrices.append(matrix)
171
-
172
- return self
173
-
174
- def adjoint(self) -> "GateSequence":
175
- """Get the complex conjugate."""
176
- # We're initializing an empty GateSequence and set the state manually, as we can
177
- # efficiently infer the adjoint values from the current value instead of recomputing them.
178
- adjoint = GateSequence()
179
- adjoint.gates = [gate.inverse() for gate in reversed(self.gates)]
180
- adjoint.labels = [inv.name for inv in adjoint.gates]
181
- adjoint.name = " ".join(adjoint.labels)
182
- adjoint.product = np.conj(self.product).T
183
- adjoint.global_phase = -self.global_phase
184
-
185
- return adjoint
186
-
187
- def copy(self) -> "GateSequence":
188
- """Create copy of the sequence of gates.
189
-
190
- Returns:
191
- A new ``GateSequence`` containing copy of list of gates.
192
-
193
- """
194
- out = type(self).__new__(type(self))
195
- out.labels = self.labels.copy()
196
- out.gates = self.gates.copy()
197
- out.matrices = self.matrices.copy()
198
- out.global_phase = self.global_phase
199
- out.product = self.product.copy()
200
- out.name = self.name
201
- out._eulers = self._eulers
202
- return out
203
-
204
- def __len__(self) -> int:
205
- """Return length of sequence of gates.
206
-
207
- Returns:
208
- Length of list containing gates.
209
- """
210
- return len(self.gates)
211
-
212
- def __getitem__(self, index: int) -> Gate:
213
- """Returns the gate at ``index`` from the list of gates.
214
-
215
- Args
216
- index: Index of gate in list that will be returned.
217
-
218
- Returns:
219
- The gate at ``index`` in the list of gates.
220
- """
221
- return self.gates[index]
222
-
223
- def __repr__(self) -> str:
224
- """Return string representation of this object.
225
-
226
- Returns:
227
- Representation of this sequence of gates.
228
- """
229
- out = "["
230
- for gate in self.gates:
231
- out += gate.name
232
- out += ", "
233
- out += "]"
234
- out += ", product: "
235
- out += str(self.product)
236
- return out
237
-
238
- def __str__(self) -> str:
239
- """Return string representation of this object.
240
-
241
- Returns:
242
- Representation of this sequence of gates.
243
- """
244
- out = "["
245
- for gate in self.gates:
246
- out += gate.name
247
- out += ", "
248
- out += "]"
249
- out += ", product: \n"
250
- out += str(self.product)
251
- return out
252
-
253
- @classmethod
254
- def from_matrix(cls, matrix: np.ndarray) -> "GateSequence":
255
- """Initialize the gate sequence from a matrix, without a gate sequence.
256
-
257
- Args:
258
- matrix: The matrix, can be SU(2) or SO(3).
259
-
260
- Returns:
261
- A ``GateSequence`` initialized from the input matrix.
262
-
263
- Raises:
264
- ValueError: If the matrix has an invalid shape.
265
- """
266
- instance = cls()
267
- if matrix.shape == (2, 2):
268
- instance.product = _convert_su2_to_so3(matrix)
269
- elif matrix.shape == (3, 3):
270
- instance.product = matrix
271
- else:
272
- raise ValueError(f"Matrix must have shape (3, 3) or (2, 2) but has {matrix.shape}.")
273
-
274
- instance.gates = []
275
- return instance
276
-
277
- def dot(self, other: "GateSequence") -> "GateSequence":
278
- """Compute the dot-product with another gate sequence.
279
-
280
- Args:
281
- other: The other gate sequence.
282
-
283
- Returns:
284
- The dot-product as gate sequence.
285
- """
286
- # We're initializing an empty GateSequence and set the state manually, as we can more
287
- # efficiently compute the multiplied values from the already constructed matrices.
288
- composed = GateSequence()
289
- composed.gates = other.gates + self.gates
290
- composed.labels = other.labels + self.labels
291
- composed.name = " ".join(composed.labels)
292
- composed.product = np.dot(self.product, other.product)
293
- composed.global_phase = self.global_phase + other.global_phase
294
-
295
- return composed
296
-
297
-
298
- def _convert_u2_to_su2(u2_matrix: np.ndarray) -> tuple[np.ndarray, float]:
299
- """Convert a U(2) matrix to SU(2) by adding a global phase."""
300
- z = 1 / np.sqrt(np.linalg.det(u2_matrix))
301
- su2_matrix = z * u2_matrix
302
- phase = np.arctan2(np.imag(z), np.real(z))
303
-
304
- return su2_matrix, phase
305
-
306
-
307
- def _compute_euler_angles_from_so3(matrix: np.ndarray) -> tuple[float, float, float]:
308
- """Computes the Euler angles from the SO(3)-matrix u.
309
-
310
- Uses the algorithm from Gregory Slabaugh,
311
- see `here <https://www.gregslabaugh.net/publications/euler.pdf>`_.
312
-
313
- Args:
314
- matrix: The SO(3)-matrix for which the Euler angles need to be computed.
315
-
316
- Returns:
317
- Tuple (phi, theta, psi), where phi is rotation about z-axis, theta rotation about y-axis
318
- and psi rotation about x-axis.
319
- """
320
- matrix = np.round(matrix, decimals=10)
321
- if matrix[2][0] != 1 and matrix[2][1] != -1:
322
- theta = -math.asin(matrix[2][0])
323
- psi = math.atan2(matrix[2][1] / math.cos(theta), matrix[2][2] / math.cos(theta))
324
- phi = math.atan2(matrix[1][0] / math.cos(theta), matrix[0][0] / math.cos(theta))
325
- return phi, theta, psi
326
- else:
327
- phi = 0
328
- if matrix[2][0] == 1:
329
- theta = math.pi / 2
330
- psi = phi + math.atan2(matrix[0][1], matrix[0][2])
331
- else:
332
- theta = -math.pi / 2
333
- psi = -phi + math.atan2(-matrix[0][1], -matrix[0][2])
334
- return phi, theta, psi
335
-
336
-
337
- def _compute_su2_from_euler_angles(angles: tuple[float, float, float]) -> np.ndarray:
338
- """Computes SU(2)-matrix from Euler angles.
339
-
340
- Args:
341
- angles: The tuple containing the Euler angles for which the corresponding SU(2)-matrix
342
- needs to be computed.
343
-
344
- Returns:
345
- The SU(2)-matrix corresponding to the Euler angles in angles.
346
- """
347
- phi, theta, psi = angles
348
- uz_phi = np.array([[np.exp(-0.5j * phi), 0], [0, np.exp(0.5j * phi)]], dtype=complex)
349
- uy_theta = np.array(
350
- [[math.cos(theta / 2), math.sin(theta / 2)], [-math.sin(theta / 2), math.cos(theta / 2)]],
351
- dtype=complex,
352
- )
353
- ux_psi = np.array(
354
- [[math.cos(psi / 2), math.sin(psi / 2) * 1j], [math.sin(psi / 2) * 1j, math.cos(psi / 2)]],
355
- dtype=complex,
356
- )
357
- return np.dot(uz_phi, np.dot(uy_theta, ux_psi))
358
-
359
-
360
- def _convert_su2_to_so3(matrix: np.ndarray) -> np.ndarray:
361
- """Computes SO(3)-matrix from input SU(2)-matrix.
362
-
363
- Args:
364
- matrix: The SU(2)-matrix for which a corresponding SO(3)-matrix needs to be computed.
365
-
366
- Returns:
367
- The SO(3)-matrix corresponding to ``matrix``.
368
-
369
- Raises:
370
- ValueError: if ``matrix`` is not an SU(2)-matrix.
371
- """
372
- _check_is_su2(matrix)
373
-
374
- matrix = matrix.astype(complex)
375
- a = np.real(matrix[0][0])
376
- b = np.imag(matrix[0][0])
377
- c = -np.real(matrix[0][1])
378
- d = -np.imag(matrix[0][1])
379
- rotation = np.array(
380
- [
381
- [a**2 - b**2 - c**2 + d**2, 2 * a * b + 2 * c * d, -2 * a * c + 2 * b * d],
382
- [-2 * a * b + 2 * c * d, a**2 - b**2 + c**2 - d**2, 2 * a * d + 2 * b * c],
383
- [2 * a * c + 2 * b * d, 2 * b * c - 2 * a * d, a**2 + b**2 - c**2 - d**2],
384
- ],
385
- dtype=float,
386
- )
387
- return rotation
388
-
389
-
390
- def _convert_so3_to_su2(matrix: np.ndarray) -> np.ndarray:
391
- """Converts an SO(3)-matrix to a corresponding SU(2)-matrix.
392
-
393
- Args:
394
- matrix: SO(3)-matrix to convert.
395
-
396
- Returns:
397
- SU(2)-matrix corresponding to SO(3)-matrix ``matrix``.
398
-
399
- Raises:
400
- ValueError: if ``matrix`` is not an SO(3)-matrix.
401
- """
402
- _check_is_so3(matrix)
403
- return _compute_su2_from_euler_angles(_compute_euler_angles_from_so3(matrix))
404
-
405
-
406
- def _check_is_su2(matrix: np.ndarray) -> None:
407
- """Check whether ``matrix`` is SU(2), otherwise raise an error."""
408
- if matrix.shape != (2, 2):
409
- raise ValueError(f"Matrix must have shape (2, 2) but has {matrix.shape}.")
410
-
411
- if abs(np.linalg.det(matrix) - 1) > 1e-4:
412
- raise ValueError(f"Determinant of matrix must be 1, but is {np.linalg.det(matrix)}.")
413
-
414
-
415
- def _check_is_so3(matrix: np.ndarray) -> None:
416
- """Check whether ``matrix`` is SO(3), otherwise raise an error."""
417
- if matrix.shape != (3, 3):
418
- raise ValueError(f"Matrix must have shape (3, 3) but has {matrix.shape}.")
419
-
420
- if abs(np.linalg.det(matrix) - 1) > 1e-4:
421
- raise ValueError(f"Determinant of matrix must be 1, but is {np.linalg.det(matrix)}.")
File without changes