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
@@ -14,42 +14,99 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import typing
18
+ import warnings
17
19
  import numpy as np
20
+ from qiskit.circuit.quantumcircuit import QuantumCircuit
21
+ from qiskit.circuit.gate import Gate
22
+ from qiskit.circuit.library import get_standard_gate_name_mapping, IGate
23
+ from qiskit.utils.deprecation import deprecate_func
24
+ from qiskit._accelerate.synthesis.discrete_basis import (
25
+ SolovayKitaevSynthesis as RustSolovayKitaevSynthesis,
26
+ GateSequence,
27
+ )
18
28
 
19
- from .gate_sequence import GateSequence
20
- from .commutator_decompose import commutator_decompose
21
- from .generate_basis_approximations import generate_basic_approximations, _1q_gates, _1q_inverses
29
+ if typing.TYPE_CHECKING:
30
+ from qiskit.dagcircuit import DAGCircuit
22
31
 
23
32
 
24
33
  class SolovayKitaevDecomposition:
25
34
  """The Solovay Kitaev discrete decomposition algorithm.
26
35
 
27
36
  This class is called recursively by the transpiler pass, which is why it is separated.
28
- See :class:`qiskit.transpiler.passes.SolovayKitaev` for more information.
37
+ See :class:`~qiskit.transpiler.passes.SolovayKitaev` for more information.
29
38
  """
30
39
 
31
40
  def __init__(
32
- self, basic_approximations: str | dict[str, np.ndarray] | list[GateSequence] | None = None
41
+ self,
42
+ basic_approximations: str | dict[str, np.ndarray] | list[GateSequence] | None = None,
43
+ *,
44
+ basis_gates: list[str | Gate] | None = None,
45
+ depth: int = 12,
46
+ check_input: bool = False,
33
47
  ) -> None:
34
48
  """
35
49
  Args:
36
50
  basic_approximations: A specification of the basic SO(3) approximations in terms
37
51
  of discrete gates. At each iteration this algorithm, the remaining error is
38
52
  approximated with the closest sequence of gates in this set.
39
- If a ``str``, this specifies a ``.npy`` filename from which to load the
53
+ If a ``str``, this specifies a filename from which to load the
40
54
  approximation. If a ``dict``, then this contains
41
55
  ``{gates: effective_SO3_matrix}`` pairs,
42
56
  e.g. ``{"h t": np.array([[0, 0.7071, -0.7071], [0, -0.7071, -0.7071], [-1, 0, 0]]}``.
43
57
  If a list, this contains the same information as the dict, but already converted to
44
58
  :class:`.GateSequence` objects, which contain the SO(3) matrix and gates.
59
+
60
+ Either this parameter, or ``basis_gates`` and ``depth`` can be specified.
61
+ basis_gates: A list of discrete (i.e., non-parameterized) standard gates.
62
+ Defaults to ``["h", "t", "tdg"]``.
63
+ depth: The number of basis gate combinations to consider in the basis set. This
64
+ determines how fast (and if) the algorithm converges and should be chosen
65
+ sufficiently high.
66
+ check_input: If ``True``, perform intermediate steps checking whether the matrices
67
+ are of expected form.
45
68
  """
46
69
  if basic_approximations is None:
47
- # generate a default basic approximation
48
- basic_approximations = generate_basic_approximations(
49
- basis_gates=["h", "t", "tdg"], depth=10
70
+ if basis_gates is not None:
71
+ basis_gates = normalize_gates(basis_gates)
72
+ self._sk = RustSolovayKitaevSynthesis(basis_gates, depth, None, check_input)
73
+
74
+ elif basis_gates is not None:
75
+ raise ValueError(
76
+ "Either basic_approximations or basis_gates + depth can be specified, not both."
50
77
  )
51
78
 
52
- self.basic_approximations = self.load_basic_approximations(basic_approximations)
79
+ else:
80
+ # Fast Rust path to load the file
81
+ if isinstance(basic_approximations, str) and basic_approximations[~3:] != ".npy":
82
+ self._sk = RustSolovayKitaevSynthesis.from_basic_approximations(
83
+ basic_approximations, True
84
+ )
85
+ else:
86
+ sequences = self.load_basic_approximations(basic_approximations)
87
+ self._sk = RustSolovayKitaevSynthesis.from_sequences(sequences, True)
88
+
89
+ self._depth = depth
90
+ self._check_input = check_input
91
+ self._basis_gates = basis_gates
92
+
93
+ @property
94
+ def depth(self) -> int:
95
+ """The maximum gate depth of the basic approximations."""
96
+ return self._depth
97
+
98
+ @property
99
+ def check_input(self) -> bool:
100
+ """Whether to perform runtime checks on the internal data."""
101
+ return self._check_input
102
+
103
+ @property
104
+ def basis_gates(self) -> list[str] | None:
105
+ """The basis gate set of the basic approximations.
106
+
107
+ If ``None``, defaults to ``["h", "t", "tdg"]``.
108
+ """
109
+ return self._basis_gates
53
110
 
54
111
  @staticmethod
55
112
  def load_basic_approximations(data: list | str | dict) -> list[GateSequence]:
@@ -71,14 +128,28 @@ class SolovayKitaevDecomposition:
71
128
  Raises:
72
129
  ValueError: If the number of gate combinations and associated matrices does not match.
73
130
  """
131
+ # new data format stored by the Rust internal class
132
+ if isinstance(data, str) and data[-4:] != ".npy":
133
+ sk = SolovayKitaevDecomposition(data)
134
+ return sk._sk.get_gate_sequences()
135
+
136
+ warnings.warn(
137
+ "It is suggested to pass basic_approximations in the binary format produced "
138
+ "by SolovayKitaevDecomposition.save_basic_approximations, which is more "
139
+ "performant than other formats. Other formats are pending deprecation "
140
+ "and will be deprecated in a future release.",
141
+ category=PendingDeprecationWarning,
142
+ )
143
+
74
144
  # is already a list of GateSequences
75
145
  if isinstance(data, list):
76
146
  return data
77
147
 
78
- # if a file, load the dictionary
148
+ # file is ``.npy``, load the dictionary it contains
79
149
  if isinstance(data, str):
80
150
  data = np.load(data, allow_pickle=True).item()
81
151
 
152
+ # parse the dictionary
82
153
  sequences = []
83
154
  for gatestring, matrix_and_phase in data.items():
84
155
  if isinstance(matrix_and_phase, tuple):
@@ -86,100 +157,100 @@ class SolovayKitaevDecomposition:
86
157
  else:
87
158
  matrix, global_phase = matrix_and_phase, 0
88
159
 
89
- sequence = GateSequence()
90
- sequence.gates = [_1q_gates[element] for element in gatestring.split()]
91
- sequence.labels = [gate.name for gate in sequence.gates]
92
- sequence.product = np.asarray(matrix)
93
- sequence.global_phase = global_phase
160
+ # gates = [_1q_gates[element] for element in gatestring.split()]
161
+ gates = normalize_gates(gatestring.split())
162
+ sequence = GateSequence.from_gates_and_matrix(gates, matrix, global_phase)
94
163
  sequences.append(sequence)
95
164
 
96
165
  return sequences
97
166
 
167
+ def save_basic_approximations(self, filename: str):
168
+ """Save the basic approximations into a file.
169
+
170
+ This can then be loaded again via the class initializer (preferred) or
171
+ via :meth:`load_basic_approximations`::
172
+
173
+ filename = "approximations.bin"
174
+ sk.save_basic_approximations(filename)
175
+
176
+ new_sk = SolovayKitaevDecomposition(filename)
177
+
178
+ Args:
179
+ filename: The filename to store the approximations in.
180
+
181
+ Raises:
182
+ ValueError: If the filename has a `.npy` extension. The format is not `.npy`,
183
+ and storing as such can cause errors when loading the file again.
184
+ """
185
+ # Safety guard: previously, we serialized via npy, but this format is incompatible
186
+ # with the current serialization, using Rust's serde + bincode. While we can still load
187
+ # .npy files in legacy format, the new format should not be stored as .npy.
188
+ if filename[~3:] == ".npy":
189
+ raise ValueError(
190
+ "The basic approximations are not stored in npy format. "
191
+ "Choose a different file extension (e.g. .bin)."
192
+ )
193
+ self._sk.save_basic_approximations(filename)
194
+
98
195
  def run(
99
196
  self,
100
- gate_matrix: np.ndarray,
197
+ gate_matrix: np.ndarray | Gate,
101
198
  recursion_degree: int,
102
199
  return_dag: bool = False,
103
200
  check_input: bool = True,
104
- ) -> "QuantumCircuit" | "DAGCircuit":
201
+ ) -> QuantumCircuit | DAGCircuit:
105
202
  r"""Run the algorithm.
106
203
 
107
204
  Args:
108
- gate_matrix: The 2x2 matrix representing the gate. This matrix has to be SU(2)
109
- up to global phase.
205
+ gate_matrix: The single-qubit gate to approximate. Can either be a :class:`.Gate`, where
206
+ :meth:`.Gate.to_matrix` returns the matrix, or a :math:`2\times 2` unitary matrix
207
+ representing the gate.
110
208
  recursion_degree: The recursion degree, called :math:`n` in the paper.
111
209
  return_dag: If ``True`` return a :class:`.DAGCircuit`, else a :class:`.QuantumCircuit`.
112
210
  check_input: If ``True`` check that the input matrix is valid for the decomposition.
211
+ Overrides the class attribute with the same name, but only for this function call.
113
212
 
114
213
  Returns:
115
214
  A one-qubit circuit approximating the ``gate_matrix`` in the specified discrete basis.
116
215
  """
117
- # make input matrix SU(2) and get the according global phase
118
- z = 1 / np.sqrt(np.linalg.det(gate_matrix))
119
-
120
- gate_matrix_su2 = z * gate_matrix
121
- gate_matrix_as_sequence = GateSequence.from_matrix(gate_matrix_su2)
122
- global_phase = np.arctan2(np.imag(z), np.real(z))
216
+ # handle overriding the check_input setting
217
+ self_check_input = self.check_input
218
+ if check_input != self_check_input:
219
+ self._sk.do_checks = check_input
123
220
 
124
- # get the decomposition as GateSequence type
125
- decomposition = self._recurse(
126
- gate_matrix_as_sequence, recursion_degree, check_input=check_input
127
- )
128
-
129
- # simplify
130
- _remove_identities(decomposition)
131
- _remove_inverse_follows_gate(decomposition)
132
-
133
- # adjust to the correct SU(2) phase
134
- adjust_phase = (
135
- np.pi if _should_adjust_phase(decomposition._to_u2(), gate_matrix_su2) else 0.0
136
- )
137
-
138
- # convert to a circuit and attach the right phases
139
- if return_dag:
140
- out = decomposition.to_dag()
221
+ if isinstance(gate_matrix, Gate):
222
+ data = self._sk.synthesize(gate_matrix, recursion_degree)
141
223
  else:
142
- out = decomposition.to_circuit()
143
-
144
- out.global_phase += adjust_phase
145
- out.global_phase -= global_phase
224
+ data = self._sk.synthesize_matrix(gate_matrix, recursion_degree)
146
225
 
147
- return out
226
+ if check_input != self_check_input:
227
+ self._sk.do_checks = self_check_input
148
228
 
149
- def _recurse(self, sequence: GateSequence, n: int, check_input: bool = True) -> GateSequence:
150
- """Performs ``n`` iterations of the Solovay-Kitaev algorithm on ``sequence``.
229
+ circuit = QuantumCircuit._from_circuit_data(data, add_regs=True)
151
230
 
152
- Args:
153
- sequence: ``GateSequence`` to which the Solovay-Kitaev algorithm is applied.
154
- n: The number of iterations that the algorithm needs to run.
155
- check_input: If ``True`` check that the input matrix represented by ``GateSequence``
156
- is valid for the decomposition.
157
-
158
- Returns:
159
- GateSequence that approximates ``sequence``.
231
+ if return_dag:
232
+ from qiskit.converters import circuit_to_dag # pylint: disable=cyclic-import
160
233
 
161
- Raises:
162
- ValueError: If the matrix in ``GateSequence`` does not represent an SO(3)-matrix.
163
- """
164
- if sequence.product.shape != (3, 3):
165
- raise ValueError("Shape of U must be (3, 3) but is", sequence.shape)
234
+ return circuit_to_dag(circuit)
166
235
 
167
- if n == 0:
168
- res = self.find_basic_approximation(sequence)
236
+ return circuit
169
237
 
238
+ def query_basic_approximation(self, gate: np.ndarray | Gate) -> QuantumCircuit:
239
+ """Query a basic approximation of a matrix."""
240
+ if isinstance(gate, Gate):
241
+ data = self._sk.query_basic_approximation(gate)
170
242
  else:
171
- u_n1 = self._recurse(sequence, n - 1, check_input=check_input)
243
+ data = self._sk.query_basic_approximation_matrix(gate)
172
244
 
173
- v_n, w_n = commutator_decompose(
174
- sequence.dot(u_n1.adjoint()).product, check_input=check_input
175
- )
176
-
177
- v_n1 = self._recurse(v_n, n - 1, check_input=check_input)
178
- w_n1 = self._recurse(w_n, n - 1, check_input=check_input)
179
- res = v_n1.dot(w_n1).dot(v_n1.adjoint()).dot(w_n1.adjoint()).dot(u_n1)
180
-
181
- return res
245
+ circuit = QuantumCircuit._from_circuit_data(data, add_regs=True)
246
+ return circuit
182
247
 
248
+ @deprecate_func(
249
+ since="2.1",
250
+ additional_msg="Use query_basic_approximation instead, which takes a Gate or matrix "
251
+ "as input and returns a QuantumCircuit object.",
252
+ pending=True,
253
+ )
183
254
  def find_basic_approximation(self, sequence: GateSequence) -> GateSequence:
184
255
  """Find ``GateSequence`` in ``self._basic_approximations`` that approximates ``sequence``.
185
256
 
@@ -187,54 +258,23 @@ class SolovayKitaevDecomposition:
187
258
  sequence: ``GateSequence`` to find the approximation to.
188
259
 
189
260
  Returns:
190
- ``GateSequence`` in ``self._basic_approximations`` that approximates ``sequence``.
261
+ ``GateSequence`` in that approximates ``sequence``.
191
262
  """
192
- # TODO explore using a k-d tree here
193
-
194
- def key(x):
195
- return np.linalg.norm(np.subtract(x.product, sequence.product))
196
-
197
- best = min(self.basic_approximations, key=key)
198
- return best
199
-
200
-
201
- def _remove_inverse_follows_gate(sequence):
202
- index = 0
203
- while index < len(sequence.gates) - 1:
204
- curr_gate = sequence.gates[index]
205
- next_gate = sequence.gates[index + 1]
206
- if curr_gate.name in _1q_inverses:
207
- remove = _1q_inverses[curr_gate.name] == next_gate.name
208
- else:
209
- remove = curr_gate.inverse() == next_gate
210
-
211
- if remove:
212
- # remove gates at index and index + 1
213
- sequence.remove_cancelling_pair([index, index + 1])
214
- # take a step back to see if we have uncovered a new pair, e.g.
215
- # [h, s, sdg, h] at index = 1 removes s, sdg but if we continue at index 1
216
- # we miss the uncovered [h, h] pair at indices 0 and 1
217
- if index > 0:
218
- index -= 1
219
- else:
220
- # next index
221
- index += 1
263
+ return self._sk.find_basic_approximation(sequence)
222
264
 
223
265
 
224
- def _remove_identities(sequence):
225
- index = 0
226
- while index < len(sequence.gates):
227
- if sequence.gates[index].name == "id":
228
- sequence.gates.pop(index)
229
- else:
230
- index += 1
266
+ def normalize_gates(gates: list[Gate | str]) -> list[Gate]:
267
+ """Normalize a list[Gate | str] into list[Gate]."""
268
+ name_to_gate = get_standard_gate_name_mapping()
269
+ # special case: we used to support "i" as IGate, but the official name is "id", so
270
+ # we add it manually here
271
+ name_to_gate["i"] = IGate()
231
272
 
273
+ def normalize(gate: Gate | str) -> Gate:
274
+ if isinstance(gate, Gate):
275
+ return gate
276
+ if gate in name_to_gate:
277
+ return name_to_gate[gate]
278
+ raise ValueError(f"Unsupported gate: {gate}")
232
279
 
233
- def _should_adjust_phase(computed: np.ndarray, target: np.ndarray) -> bool:
234
- """
235
- The implemented SolovayKitaevDecomposition has a global phase uncertainty of +-1,
236
- due to approximating not the original SU(2) matrix but its projection onto SO(3).
237
- This function returns ``True`` if the global phase of the computed approximation
238
- should be adjusted (by adding pi) to better much the target.
239
- """
240
- return np.linalg.norm(-computed - target) < np.linalg.norm(computed - target)
280
+ return list(map(normalize, gates))
@@ -17,7 +17,8 @@ from __future__ import annotations
17
17
  from collections.abc import Callable
18
18
  from typing import Any
19
19
  from qiskit.circuit.quantumcircuit import QuantumCircuit
20
- from qiskit.quantum_info.operators import SparsePauliOp, Pauli
20
+ from qiskit.quantum_info.operators import SparsePauliOp
21
+ import qiskit.quantum_info
21
22
 
22
23
  from .suzuki_trotter import SuzukiTrotter
23
24
 
@@ -55,7 +56,8 @@ class LieTrotter(SuzukiTrotter):
55
56
  insert_barriers: bool = False,
56
57
  cx_structure: str = "chain",
57
58
  atomic_evolution: (
58
- Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
59
+ Callable[[QuantumCircuit, qiskit.quantum_info.Pauli | SparsePauliOp, float], None]
60
+ | None
59
61
  ) = None,
60
62
  wrap: bool = False,
61
63
  preserve_order: bool = True,
@@ -70,11 +72,12 @@ class LieTrotter(SuzukiTrotter):
70
72
  ``"chain"``, where next neighbor connections are used, or ``"fountain"``,
71
73
  where all qubits are connected to one. This only takes effect when
72
74
  ``atomic_evolution is None``.
73
- atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
74
- :class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
75
- three arguments: the circuit to append the evolution to, the Pauli operator to
76
- evolve, and the evolution time. By default, a single Pauli evolution is decomposed
77
- into a chain of ``CX`` gates and a single ``RZ`` gate.
75
+ atomic_evolution: A function to apply the evolution of a single
76
+ :class:`~.quantum_info.Pauli`, or :class:`.SparsePauliOp` of only commuting terms,
77
+ to a circuit. The function takes in three arguments: the circuit to append the
78
+ evolution to, the Pauli operator to evolve, and the evolution time. By default, a
79
+ single Pauli evolution is decomposed into a chain of ``CX`` gates and a single
80
+ ``RZ`` gate.
78
81
  wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
79
82
  effect when ``atomic_evolution is None``.
80
83
  preserve_order: If ``False``, allows reordering the terms of the operator to
@@ -24,8 +24,9 @@ import numpy as np
24
24
  import rustworkx as rx
25
25
  from qiskit.circuit.parameterexpression import ParameterExpression
26
26
  from qiskit.circuit.quantumcircuit import QuantumCircuit, ParameterValueType
27
- from qiskit.quantum_info import SparsePauliOp, Pauli, SparseObservable
27
+ from qiskit.quantum_info import SparsePauliOp, SparseObservable
28
28
  from qiskit._accelerate.circuit_library import pauli_evolution
29
+ import qiskit.quantum_info
29
30
 
30
31
  from .evolution_synthesis import EvolutionSynthesis
31
32
 
@@ -48,7 +49,8 @@ class ProductFormula(EvolutionSynthesis):
48
49
  insert_barriers: bool = False,
49
50
  cx_structure: str = "chain",
50
51
  atomic_evolution: (
51
- Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
52
+ Callable[[QuantumCircuit, qiskit.quantum_info.Pauli | SparsePauliOp, float], None]
53
+ | None
52
54
  ) = None,
53
55
  wrap: bool = False,
54
56
  preserve_order: bool = True,
@@ -64,11 +66,12 @@ class ProductFormula(EvolutionSynthesis):
64
66
  ``"chain"``, where next neighbor connections are used, or ``"fountain"``,
65
67
  where all qubits are connected to one. This only takes effect when
66
68
  ``atomic_evolution is None``.
67
- atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
68
- :class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
69
- three arguments: the circuit to append the evolution to, the Pauli operator to
70
- evolve, and the evolution time. By default, a single Pauli evolution is decomposed
71
- into a chain of ``CX`` gates and a single ``RZ`` gate.
69
+ atomic_evolution: A function to apply the evolution of a single
70
+ :class:`~.quantum_info.Pauli`, or :class:`.SparsePauliOp` of only commuting terms,
71
+ to a circuit. The function takes in three arguments: the circuit to append the
72
+ evolution to, the Pauli operator to evolve, and the evolution time. By default, a
73
+ single Pauli evolution is decomposed into a chain of ``CX`` gates and a single
74
+ ``RZ`` gate.
72
75
  wrap: Whether to wrap the atomic evolutions into custom gate objects. Note that setting
73
76
  this to ``True`` is slower than ``False``. This only takes effect when
74
77
  ``atomic_evolution is None``.
@@ -20,8 +20,9 @@ from itertools import chain
20
20
  from collections.abc import Callable
21
21
  import numpy as np
22
22
  from qiskit.circuit.quantumcircuit import QuantumCircuit
23
- from qiskit.quantum_info.operators import SparsePauliOp, Pauli
23
+ from qiskit.quantum_info.operators import SparsePauliOp
24
24
  from qiskit.exceptions import QiskitError
25
+ import qiskit.quantum_info
25
26
 
26
27
  from .product_formula import ProductFormula, reorder_paulis
27
28
 
@@ -45,7 +46,8 @@ class QDrift(ProductFormula):
45
46
  insert_barriers: bool = False,
46
47
  cx_structure: str = "chain",
47
48
  atomic_evolution: (
48
- Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
49
+ Callable[[QuantumCircuit, qiskit.quantum_info.Pauli | SparsePauliOp, float], None]
50
+ | None
49
51
  ) = None,
50
52
  seed: int | None = None,
51
53
  wrap: bool = False,
@@ -61,11 +63,12 @@ class QDrift(ProductFormula):
61
63
  ``"chain"``, where next neighbor connections are used, or ``"fountain"``, where all
62
64
  qubits are connected to one. This only takes effect when
63
65
  ``atomic_evolution is None``.
64
- atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
65
- :class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
66
- three arguments: the circuit to append the evolution to, the Pauli operator to
67
- evolve, and the evolution time. By default, a single Pauli evolution is decomposed
68
- into a chain of ``CX`` gates and a single ``RZ`` gate.
66
+ atomic_evolution: A function to apply the evolution of a single
67
+ :class:`~.quantum_info.Pauli`, or :class:`.SparsePauliOp` of only commuting terms,
68
+ to a circuit. The function takes in three arguments: the circuit to append the
69
+ evolution to, the Pauli operator to evolve, and the evolution time. By default, a
70
+ single Pauli evolution is decomposed into a chain of ``CX`` gates and a single
71
+ ``RZ`` gate.
69
72
  seed: An optional seed for reproducibility of the random sampling process.
70
73
  wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
71
74
  effect when ``atomic_evolution is None``.
@@ -21,7 +21,8 @@ import numpy as np
21
21
 
22
22
  from qiskit.circuit.parameterexpression import ParameterExpression
23
23
  from qiskit.circuit.quantumcircuit import QuantumCircuit
24
- from qiskit.quantum_info import SparsePauliOp, Pauli
24
+ from qiskit.quantum_info import SparsePauliOp
25
+ import qiskit.quantum_info
25
26
 
26
27
  from .product_formula import ProductFormula, reorder_paulis
27
28
 
@@ -65,7 +66,8 @@ class SuzukiTrotter(ProductFormula):
65
66
  insert_barriers: bool = False,
66
67
  cx_structure: str = "chain",
67
68
  atomic_evolution: (
68
- Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
69
+ Callable[[QuantumCircuit, qiskit.quantum_info.Pauli | SparsePauliOp, float], None]
70
+ | None
69
71
  ) = None,
70
72
  wrap: bool = False,
71
73
  preserve_order: bool = True,
@@ -80,11 +82,12 @@ class SuzukiTrotter(ProductFormula):
80
82
  cx_structure: How to arrange the CX gates for the Pauli evolutions, can be ``"chain"``,
81
83
  where next neighbor connections are used, or ``"fountain"``, where all qubits are
82
84
  connected to one. This only takes effect when ``atomic_evolution is None``.
83
- atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
84
- :class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
85
- three arguments: the circuit to append the evolution to, the Pauli operator to
86
- evolve, and the evolution time. By default, a single Pauli evolution is decomposed
87
- into a chain of ``CX`` gates and a single ``RZ`` gate.
85
+ atomic_evolution: A function to apply the evolution of a single
86
+ :class:`~.quantum_info.Pauli`, or :class:`.SparsePauliOp` of only commuting terms,
87
+ to a circuit. The function takes in three arguments: the circuit to append the
88
+ evolution to, the Pauli operator to evolve, and the evolution time. By default, a
89
+ single Pauli evolution is decomposed into a chain of ``CX`` gates and a single
90
+ ``RZ`` gate.
88
91
  wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
89
92
  effect when ``atomic_evolution is None``.
90
93
  preserve_order: If ``False``, allows reordering the terms of the operator to
@@ -14,6 +14,10 @@
14
14
 
15
15
  from .mcmt_vchain import synth_mcmt_vchain
16
16
  from .mcx_synthesis import (
17
+ synth_mcx_1_clean_kg24,
18
+ synth_mcx_1_dirty_kg24,
19
+ synth_mcx_2_clean_kg24,
20
+ synth_mcx_2_dirty_kg24,
17
21
  synth_mcx_n_dirty_i15,
18
22
  synth_mcx_n_clean_m15,
19
23
  synth_mcx_1_clean_b95,