qiskit 2.0.2__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 (185) 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 +5 -5
  15. qiskit/circuit/library/arithmetic/multipliers/multiplier.py +3 -3
  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/quantum_volume.py +2 -1
  52. qiskit/circuit/library/standard_gates/dcx.py +11 -12
  53. qiskit/circuit/library/standard_gates/ecr.py +21 -24
  54. qiskit/circuit/library/standard_gates/equivalence_library.py +232 -96
  55. qiskit/circuit/library/standard_gates/global_phase.py +5 -6
  56. qiskit/circuit/library/standard_gates/h.py +22 -45
  57. qiskit/circuit/library/standard_gates/i.py +1 -1
  58. qiskit/circuit/library/standard_gates/iswap.py +13 -31
  59. qiskit/circuit/library/standard_gates/p.py +19 -26
  60. qiskit/circuit/library/standard_gates/r.py +11 -17
  61. qiskit/circuit/library/standard_gates/rx.py +21 -45
  62. qiskit/circuit/library/standard_gates/rxx.py +7 -22
  63. qiskit/circuit/library/standard_gates/ry.py +21 -39
  64. qiskit/circuit/library/standard_gates/ryy.py +13 -28
  65. qiskit/circuit/library/standard_gates/rz.py +18 -35
  66. qiskit/circuit/library/standard_gates/rzx.py +7 -22
  67. qiskit/circuit/library/standard_gates/rzz.py +7 -19
  68. qiskit/circuit/library/standard_gates/s.py +44 -39
  69. qiskit/circuit/library/standard_gates/swap.py +25 -38
  70. qiskit/circuit/library/standard_gates/sx.py +34 -41
  71. qiskit/circuit/library/standard_gates/t.py +18 -27
  72. qiskit/circuit/library/standard_gates/u.py +8 -24
  73. qiskit/circuit/library/standard_gates/u1.py +28 -52
  74. qiskit/circuit/library/standard_gates/u2.py +9 -9
  75. qiskit/circuit/library/standard_gates/u3.py +24 -40
  76. qiskit/circuit/library/standard_gates/x.py +190 -336
  77. qiskit/circuit/library/standard_gates/xx_minus_yy.py +12 -50
  78. qiskit/circuit/library/standard_gates/xx_plus_yy.py +13 -52
  79. qiskit/circuit/library/standard_gates/y.py +19 -23
  80. qiskit/circuit/library/standard_gates/z.py +31 -38
  81. qiskit/circuit/parameter.py +14 -5
  82. qiskit/circuit/parameterexpression.py +109 -75
  83. qiskit/circuit/quantumcircuit.py +174 -101
  84. qiskit/circuit/quantumcircuitdata.py +1 -0
  85. qiskit/circuit/random/__init__.py +37 -2
  86. qiskit/circuit/random/utils.py +445 -56
  87. qiskit/circuit/tools/pi_check.py +5 -13
  88. qiskit/compiler/transpiler.py +1 -1
  89. qiskit/converters/circuit_to_instruction.py +2 -2
  90. qiskit/dagcircuit/__init__.py +11 -0
  91. qiskit/dagcircuit/collect_blocks.py +10 -6
  92. qiskit/dagcircuit/dagnode.py +8 -3
  93. qiskit/primitives/__init__.py +2 -2
  94. qiskit/primitives/base/base_estimator.py +2 -2
  95. qiskit/primitives/containers/data_bin.py +0 -3
  96. qiskit/primitives/containers/observables_array.py +192 -108
  97. qiskit/primitives/primitive_job.py +29 -10
  98. qiskit/providers/fake_provider/generic_backend_v2.py +2 -0
  99. qiskit/qasm3/__init__.py +106 -12
  100. qiskit/qasm3/ast.py +15 -1
  101. qiskit/qasm3/exporter.py +59 -36
  102. qiskit/qasm3/printer.py +12 -0
  103. qiskit/qpy/__init__.py +183 -7
  104. qiskit/qpy/binary_io/circuits.py +256 -24
  105. qiskit/qpy/binary_io/parse_sympy_repr.py +5 -0
  106. qiskit/qpy/binary_io/schedules.py +12 -32
  107. qiskit/qpy/binary_io/value.py +36 -18
  108. qiskit/qpy/common.py +11 -3
  109. qiskit/qpy/formats.py +17 -1
  110. qiskit/qpy/interface.py +52 -12
  111. qiskit/qpy/type_keys.py +7 -1
  112. qiskit/quantum_info/__init__.py +10 -0
  113. qiskit/quantum_info/operators/__init__.py +1 -0
  114. qiskit/quantum_info/operators/symplectic/__init__.py +1 -0
  115. qiskit/quantum_info/operators/symplectic/clifford_circuits.py +26 -0
  116. qiskit/quantum_info/operators/symplectic/pauli.py +2 -2
  117. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +1 -1
  118. qiskit/result/sampled_expval.py +3 -1
  119. qiskit/synthesis/__init__.py +10 -0
  120. qiskit/synthesis/arithmetic/__init__.py +1 -1
  121. qiskit/synthesis/arithmetic/adders/__init__.py +1 -0
  122. qiskit/synthesis/arithmetic/adders/draper_qft_adder.py +6 -2
  123. qiskit/synthesis/arithmetic/adders/rv_ripple_carry_adder.py +156 -0
  124. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +14 -126
  125. qiskit/synthesis/discrete_basis/solovay_kitaev.py +161 -121
  126. qiskit/synthesis/evolution/lie_trotter.py +10 -7
  127. qiskit/synthesis/evolution/product_formula.py +10 -7
  128. qiskit/synthesis/evolution/qdrift.py +10 -7
  129. qiskit/synthesis/evolution/suzuki_trotter.py +10 -7
  130. qiskit/synthesis/multi_controlled/__init__.py +4 -0
  131. qiskit/synthesis/multi_controlled/mcx_synthesis.py +402 -178
  132. qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py +14 -15
  133. qiskit/synthesis/qft/qft_decompose_lnn.py +7 -25
  134. qiskit/synthesis/unitary/qsd.py +80 -9
  135. qiskit/transpiler/__init__.py +19 -8
  136. qiskit/transpiler/instruction_durations.py +2 -20
  137. qiskit/transpiler/passes/__init__.py +5 -2
  138. qiskit/transpiler/passes/layout/apply_layout.py +2 -2
  139. qiskit/transpiler/passes/layout/dense_layout.py +26 -6
  140. qiskit/transpiler/passes/layout/disjoint_utils.py +1 -166
  141. qiskit/transpiler/passes/layout/sabre_layout.py +22 -3
  142. qiskit/transpiler/passes/layout/sabre_pre_layout.py +1 -1
  143. qiskit/transpiler/passes/layout/vf2_layout.py +49 -13
  144. qiskit/transpiler/passes/layout/vf2_utils.py +13 -1
  145. qiskit/transpiler/passes/optimization/__init__.py +1 -1
  146. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +2 -1
  147. qiskit/transpiler/passes/optimization/optimize_clifford_t.py +68 -0
  148. qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +3 -9
  149. qiskit/transpiler/passes/routing/sabre_swap.py +12 -2
  150. qiskit/transpiler/passes/routing/star_prerouting.py +106 -81
  151. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  152. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
  153. qiskit/transpiler/passes/scheduling/padding/__init__.py +1 -0
  154. qiskit/transpiler/passes/scheduling/padding/context_aware_dynamical_decoupling.py +876 -0
  155. qiskit/transpiler/passes/synthesis/__init__.py +1 -0
  156. qiskit/transpiler/passes/synthesis/clifford_unitary_synth_plugin.py +123 -0
  157. qiskit/transpiler/passes/synthesis/hls_plugins.py +502 -96
  158. qiskit/transpiler/passes/synthesis/plugin.py +4 -0
  159. qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +27 -22
  160. qiskit/transpiler/passmanager_config.py +3 -0
  161. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +149 -28
  162. qiskit/transpiler/preset_passmanagers/common.py +101 -0
  163. qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +6 -0
  164. qiskit/transpiler/preset_passmanagers/level3.py +2 -2
  165. qiskit/transpiler/target.py +15 -2
  166. qiskit/utils/optionals.py +6 -5
  167. qiskit/visualization/circuit/_utils.py +5 -3
  168. qiskit/visualization/circuit/latex.py +9 -2
  169. qiskit/visualization/circuit/matplotlib.py +26 -4
  170. qiskit/visualization/circuit/qcstyle.py +9 -157
  171. qiskit/visualization/dag/__init__.py +13 -0
  172. qiskit/visualization/dag/dagstyle.py +103 -0
  173. qiskit/visualization/dag/styles/__init__.py +13 -0
  174. qiskit/visualization/dag/styles/color.json +10 -0
  175. qiskit/visualization/dag/styles/plain.json +5 -0
  176. qiskit/visualization/dag_visualization.py +169 -98
  177. qiskit/visualization/style.py +223 -0
  178. {qiskit-2.0.2.dist-info → qiskit-2.1.0.dist-info}/METADATA +14 -13
  179. {qiskit-2.0.2.dist-info → qiskit-2.1.0.dist-info}/RECORD +183 -174
  180. {qiskit-2.0.2.dist-info → qiskit-2.1.0.dist-info}/entry_points.txt +6 -0
  181. qiskit/synthesis/discrete_basis/commutator_decompose.py +0 -265
  182. qiskit/synthesis/discrete_basis/gate_sequence.py +0 -421
  183. {qiskit-2.0.2.dist-info → qiskit-2.1.0.dist-info}/WHEEL +0 -0
  184. {qiskit-2.0.2.dist-info → qiskit-2.1.0.dist-info}/licenses/LICENSE.txt +0 -0
  185. {qiskit-2.0.2.dist-info → qiskit-2.1.0.dist-info}/top_level.txt +0 -0
@@ -171,30 +171,29 @@ def _mcsu2_real_diagonal(
171
171
  k_1 = math.ceil(num_controls / 2.0)
172
172
  k_2 = math.floor(num_controls / 2.0)
173
173
 
174
- circuit = QuantumCircuit(num_controls + 1, name="MCSU2")
175
174
  controls = list(range(num_controls)) # control indices, defined for code legibility
176
175
  target = num_controls # target index, defined for code legibility
177
176
 
177
+ mcx1 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_1)
178
+ mcx1_num_ancillas = mcx1.num_qubits - k_1 - 1
179
+ mcx1_qubits = controls[:k_1] + [target] + controls[k_1 : k_1 + mcx1_num_ancillas]
180
+
181
+ mcx2 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_2)
182
+ mcx2_num_ancillas = mcx2.num_qubits - k_2 - 1
183
+ mcx2_qubits = controls[k_1:] + [target] + controls[k_1 - mcx2_num_ancillas : k_1]
184
+
185
+ circuit = QuantumCircuit(num_controls + 1, name="MCSU2")
186
+
178
187
  if not is_secondary_diag_real:
179
188
  circuit.h(target)
180
189
 
181
- mcx_1 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_1)
182
- circuit.compose(mcx_1, controls[:k_1] + [target] + controls[k_1 : 2 * k_1 - 2], inplace=True)
190
+ circuit.compose(mcx1, mcx1_qubits, inplace=True)
183
191
  circuit.append(s_gate, [target])
184
-
185
- # TODO: improve CX count by using action_only=True (based on #9687)
186
- mcx_2 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_2).to_gate()
187
- circuit.compose(
188
- mcx_2.inverse(), controls[k_1:] + [target] + controls[k_1 - k_2 + 2 : k_1], inplace=True
189
- )
192
+ circuit.compose(mcx2, mcx2_qubits, inplace=True)
190
193
  circuit.append(s_gate.inverse(), [target])
191
-
192
- mcx_3 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_1).to_gate()
193
- circuit.compose(mcx_3, controls[:k_1] + [target] + controls[k_1 : 2 * k_1 - 2], inplace=True)
194
+ circuit.compose(mcx1, mcx1_qubits, inplace=True)
194
195
  circuit.append(s_gate, [target])
195
-
196
- mcx_4 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_2).to_gate()
197
- circuit.compose(mcx_4, controls[k_1:] + [target] + controls[k_1 - k_2 + 2 : k_1], inplace=True)
196
+ circuit.compose(mcx2, mcx2_qubits, inplace=True)
198
197
  circuit.append(s_gate.inverse(), [target])
199
198
 
200
199
  if not is_secondary_diag_real:
@@ -13,9 +13,9 @@
13
13
  Circuit synthesis for a QFT circuit.
14
14
  """
15
15
 
16
- import numpy as np
17
16
  from qiskit.circuit import QuantumCircuit
18
- from qiskit.synthesis.permutation.permutation_reverse_lnn import _append_reverse_permutation_lnn_kms
17
+ from qiskit._accelerate.synthesis.qft import synth_qft_line as _synth_qft_line
18
+
19
19
  from .qft_decompose_full import _warn_if_precision_loss
20
20
 
21
21
 
@@ -53,27 +53,9 @@ def synth_qft_line(
53
53
  `arXiv:quant-ph/0402196 [quant-ph] <https://arxiv.org/abs/quant-ph/0402196>`_
54
54
  """
55
55
  _warn_if_precision_loss(num_qubits - approximation_degree - 1)
56
- qc = QuantumCircuit(num_qubits)
57
-
58
- for i in range(num_qubits):
59
- qc.h(num_qubits - 1)
60
-
61
- for j in range(i, num_qubits - 1):
62
- if j - i + 2 < num_qubits - approximation_degree + 1:
63
- qc.p(np.pi / 2 ** (j - i + 2), num_qubits - j + i - 1)
64
- qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
65
- qc.p(-np.pi / 2 ** (j - i + 2), num_qubits - j + i - 2)
66
- qc.cx(num_qubits - j + i - 2, num_qubits - j + i - 1)
67
- qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
68
- qc.p(np.pi / 2 ** (j - i + 2), num_qubits - j + i - 1)
69
- else:
70
- qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
71
- qc.cx(num_qubits - j + i - 2, num_qubits - j + i - 1)
72
- qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
73
-
74
- if not do_swaps:
75
- # Add a reversal network for LNN connectivity in depth 2*n+2,
76
- # based on Kutin at al., https://arxiv.org/abs/quant-ph/0701194, Section 5.
77
- _append_reverse_permutation_lnn_kms(qc, num_qubits)
78
56
 
79
- return qc
57
+ return QuantumCircuit._from_circuit_data(
58
+ # From rust
59
+ _synth_qft_line(num_qubits, do_swaps, approximation_degree),
60
+ add_regs=True,
61
+ )
@@ -15,7 +15,6 @@ Quantum Shannon Decomposition.
15
15
  Method is described in arXiv:quant-ph/0406176.
16
16
  """
17
17
  from __future__ import annotations
18
- import math
19
18
  from typing import Callable
20
19
  import scipy
21
20
  import numpy as np
@@ -31,6 +30,7 @@ from qiskit.circuit.library.generalized_gates.uc_pauli_rot import UCPauliRotGate
31
30
  from qiskit.circuit.library.generalized_gates.ucry import UCRYGate
32
31
  from qiskit.circuit.library.generalized_gates.ucrz import UCRZGate
33
32
  from qiskit._accelerate.two_qubit_decompose import two_qubit_decompose_up_to_diagonal
33
+ from qiskit._accelerate.cos_sin_decomp import cossin
34
34
 
35
35
 
36
36
  def qs_decomposition(
@@ -96,7 +96,8 @@ def qs_decomposition(
96
96
  """
97
97
  # _depth (int): Internal use parameter to track recursion depth.
98
98
  dim = mat.shape[0]
99
- nqubits = int(math.log2(dim))
99
+ nqubits = dim.bit_length() - 1
100
+
100
101
  if np.allclose(np.identity(dim), mat):
101
102
  return QuantumCircuit(nqubits)
102
103
  if dim == 2:
@@ -121,11 +122,25 @@ def qs_decomposition(
121
122
  decomposer_2q = TwoQubitBasisDecomposer(CXGate())
122
123
  circ = decomposer_2q(mat)
123
124
  else:
125
+ # check whether matrix is equivalent to block diagonal wrt ctrl_index
126
+ if opt_a2 is False:
127
+ for ctrl_index in range(nqubits):
128
+ um00, um11, um01, um10 = _extract_multiplex_blocks(mat, ctrl_index)
129
+ # the ctrl_index is reversed here
130
+ if _off_diagonals_are_zero(um01, um10):
131
+ decirc = _demultiplex(
132
+ um00,
133
+ um11,
134
+ opt_a1=opt_a1,
135
+ opt_a2=opt_a2,
136
+ _depth=_depth,
137
+ _ctrl_index=nqubits - 1 - ctrl_index,
138
+ )
139
+ return decirc
124
140
  qr = QuantumRegister(nqubits)
125
141
  circ = QuantumCircuit(qr)
126
- dim_o2 = dim // 2
127
142
  # perform cosine-sine decomposition
128
- (u1, u2), vtheta, (v1h, v2h) = scipy.linalg.cossin(mat, separate=True, p=dim_o2, q=dim_o2)
143
+ (u1, u2), vtheta, (v1h, v2h) = cossin(np.asarray(mat, dtype=complex))
129
144
  # left circ
130
145
  left_circ = _demultiplex(v1h, v2h, opt_a1=opt_a1, opt_a2=opt_a2, _depth=_depth)
131
146
  circ.append(left_circ.to_instruction(), qr)
@@ -150,7 +165,7 @@ def qs_decomposition(
150
165
  return circ
151
166
 
152
167
 
153
- def _demultiplex(um0, um1, opt_a1=False, opt_a2=False, *, _depth=0):
168
+ def _demultiplex(um0, um1, opt_a1=False, opt_a2=False, *, _depth=0, _ctrl_index=None):
154
169
  """Decompose a generic multiplexer.
155
170
 
156
171
  ────□────
@@ -183,12 +198,17 @@ def _demultiplex(um0, um1, opt_a1=False, opt_a2=False, *, _depth=0):
183
198
  unitaries into a diagonal gate and a two cx unitary and reduces overall cx count by
184
199
  4^(n-2) - 1.
185
200
  _depth (int): This is an internal variable to track the recursion depth.
201
+ _ctrl_index (int): The index wrt which um0 and um1 are controlled.
186
202
 
187
203
  Returns:
188
204
  QuantumCircuit: decomposed circuit
189
205
  """
190
206
  dim = um0.shape[0] + um1.shape[0] # these should be same dimension
191
- nqubits = int(math.log2(dim))
207
+ nqubits = dim.bit_length() - 1
208
+ if _ctrl_index is None:
209
+ _ctrl_index = nqubits - 1
210
+ layout = list(range(0, _ctrl_index)) + list(range(_ctrl_index + 1, nqubits)) + [_ctrl_index]
211
+
192
212
  um0um1 = um0 @ um1.T.conjugate()
193
213
  if is_hermitian_matrix(um0um1):
194
214
  eigvals, vmat = scipy.linalg.eigh(um0um1)
@@ -205,18 +225,18 @@ def _demultiplex(um0, um1, opt_a1=False, opt_a2=False, *, _depth=0):
205
225
  left_gate = qs_decomposition(
206
226
  wmat, opt_a1=opt_a1, opt_a2=opt_a2, _depth=_depth + 1
207
227
  ).to_instruction()
208
- circ.append(left_gate, range(nqubits - 1))
228
+ circ.append(left_gate, layout[: nqubits - 1])
209
229
 
210
230
  # multiplexed Rz
211
231
  angles = 2 * np.angle(np.conj(dvals))
212
232
  ucrz = UCRZGate(angles.tolist())
213
- circ.append(ucrz, [nqubits - 1] + list(range(nqubits - 1)))
233
+ circ.append(ucrz, [layout[-1]] + layout[: nqubits - 1])
214
234
 
215
235
  # right gate
216
236
  right_gate = qs_decomposition(
217
237
  vmat, opt_a1=opt_a1, opt_a2=opt_a2, _depth=_depth + 1
218
238
  ).to_instruction()
219
- circ.append(right_gate, range(nqubits - 1))
239
+ circ.append(right_gate, layout[: nqubits - 1])
220
240
 
221
241
  return circ
222
242
 
@@ -286,3 +306,54 @@ def _apply_a2(circ):
286
306
  qc3 = two_qubit_decompose.two_qubit_cnot_decompose(mat2)
287
307
  ccirc.data[ind2] = ccirc.data[ind2].replace(operation=qc3.to_gate())
288
308
  return ccirc
309
+
310
+
311
+ def _extract_multiplex_blocks(umat, k):
312
+ """
313
+ A block diagonal gate is represented as:
314
+ [ um00 | um01 ]
315
+ [ ---- | ---- ]
316
+ [ um10 | um11 ]
317
+ Args:
318
+ umat (ndarray): unitary matrix
319
+ k (integer): qubit which indicates the ctrl index
320
+ Returns:
321
+ um00 (ndarray): upper left block
322
+ um01 (ndarray): upper right block
323
+ um10 (ndarray): lower left block
324
+ um11 (ndarray): lower right block
325
+ """
326
+ dim = umat.shape[0]
327
+ nqubits = dim.bit_length() - 1
328
+ halfdim = dim // 2
329
+
330
+ utensor = umat.reshape((2,) * (2 * nqubits))
331
+
332
+ # Move qubit k to top
333
+ if k != 0:
334
+ utensor = np.moveaxis(utensor, k, 0)
335
+ utensor = np.moveaxis(utensor, k + nqubits, nqubits)
336
+
337
+ # reshape for extraction
338
+ ud4 = utensor.reshape(2, halfdim, 2, halfdim)
339
+ # block for qubit k = |0>
340
+ um00 = ud4[0, :, 0, :]
341
+ # block for qubit k = |1>
342
+ um11 = ud4[1, :, 1, :]
343
+ # off diagonal blocks
344
+ um01 = ud4[0, :, 1, :]
345
+ um10 = ud4[1, :, 0, :]
346
+ return um00, um11, um01, um10
347
+
348
+
349
+ def _off_diagonals_are_zero(um01, um10, atol=1e-12):
350
+ """
351
+ Checks whether off-diagonal blocks are zero.
352
+ Args:
353
+ um01 (ndarray): upper right block
354
+ um10 (ndarray): lower left block
355
+ atol (float): absolute tolerance
356
+ Returns:
357
+ bool: whether both blocks are zero within tolerance
358
+ """
359
+ return np.allclose(um01, 0, atol=atol) and np.allclose(um10, 0, atol=atol)
@@ -237,7 +237,7 @@ Initialization stage
237
237
  --------------------
238
238
 
239
239
  .. seealso::
240
- `Init stage explanation <https://docs.quantum.ibm.com/guides/transpiler-stages#init-stage>`__
240
+ `Init stage explanation <https://quantum.cloud.ibm.com/docs/guides/transpiler-stages#init-stage>`__
241
241
  Higher-level user-facing explanation of the init stage in the IBM Quantum guide.
242
242
 
243
243
  The ``init`` stage is responsible for high-level, logical optimizations on abstract circuits, and
@@ -291,9 +291,11 @@ Layout stage
291
291
  ------------
292
292
 
293
293
  .. seealso::
294
- `Layout stage explanation <https://docs.quantum.ibm.com/guides/transpiler-stages#layout-stage>`__
294
+ `Layout stage explanation`__
295
295
  Higher-level user-facing explanation of the layout stage in the IBM Quantum guide.
296
296
 
297
+ __ https://quantum.cloud.ibm.com/docs/guides/transpiler-stages#layout-stage
298
+
297
299
  The layout stage is responsible for making an initial mapping between the virtual qubits of the
298
300
  input circuit, and the hardware qubits of the target. This includes expanding the input circuit
299
301
  with explicit ancillas so it has as many qubits as the target has, and rewriting all operations in
@@ -452,9 +454,11 @@ Routing stage
452
454
  -------------
453
455
 
454
456
  .. seealso::
455
- `Routing stage explanation <https://docs.quantum.ibm.com/guides/transpiler-stages#routing-stage>`__
457
+ `Routing stage explanation`__
456
458
  Higher-level user-facing explanation of the routing stage in the IBM Quantum guide.
457
459
 
460
+ __ https://quantum.cloud.ibm.com/docs/guides/transpiler-stages#routing-stage
461
+
458
462
  The routing stage ensures that the virtual connectivity graph of the circuit is compatible with the
459
463
  hardware connectivity graph of the target. In simpler terms, the routing stage makes sure that all
460
464
  two-qubit gates in the circuit are mapped to hardware qubits that have a defined two-qubit operation
@@ -598,7 +602,7 @@ Translation stage
598
602
  `Translation stage explanation`__
599
603
  Higher-level user-facing explanation of the translation stage in the IBM Quantum guide.
600
604
 
601
- .. __: https://docs.quantum.ibm.com/guides/transpiler-stages#translation-stage
605
+ .. __: https://quantum.cloud.ibm.com/docs/guides/transpiler-stages#translation-stage
602
606
 
603
607
  The translation stage is responsible for rewriting all gates in the circuit into ones that are
604
608
  supported by the target ISA. For example, if a ``cx`` is requested on hardware qubits 0 and 1, but
@@ -673,6 +677,9 @@ At a high level, this starts from the set of gates requested by the circuit, and
673
677
  given :class:`.EquivalenceLibrary` (typically the :data:`.SessionEquivalenceLibrary`) to move
674
678
  towards the ISA.
675
679
 
680
+ For a Clifford+T basis set, the single-qubit rotation gates are approximated using the
681
+ :class:`.SolovayKitaevDecomposition` algorithm.
682
+
676
683
  This is the default translation method.
677
684
 
678
685
  The optimization level has no effect on this plugin.
@@ -687,7 +694,7 @@ Optimization stage
687
694
  `Optimization stage explanation`__
688
695
  Higher-level user-facing explanation of the optimization stage in the IBM Quantum guide.
689
696
 
690
- .. __: https://docs.quantum.ibm.com/guides/transpiler-stages#optimization-stage
697
+ .. __: https://quantum.cloud.ibm.com/docs/guides/transpiler-stages#optimization-stage
691
698
 
692
699
  The optimization stage is for low-level hardware-aware optimizations. Unlike :ref:`the init stage
693
700
  <transpiler-preset-stage-init>`, the input to this stage is a circuit that is already
@@ -700,7 +707,7 @@ as a fix-up pipeline.
700
707
 
701
708
  Qiskit's built-in optimization plugins are general, and apply well to most real-world ISAs for
702
709
  non-error-corrected devices. The built-in plugins are less well-suited to ISAs that have no
703
- continuously parametrized single-qubit gate, such as a Clifford+T basis set.
710
+ continuously parametrized single-qubit gate.
704
711
 
705
712
  When writing :ref:`stage plugins <transpiler-preset-stage-plugins>`, the entry point for
706
713
  ``optimization`` is ``qiskit.transpiler.optimization``. The built-in plugins are:
@@ -720,10 +727,12 @@ When writing :ref:`stage plugins <transpiler-preset-stage-plugins>`, the entry p
720
727
  Built-in ``default`` plugin
721
728
  ...........................
722
729
 
723
- This varies significantly between optimization levels.
730
+ This varies significantly depending on the optimization level and whether the basis set is of the
731
+ form Clifford+T.
724
732
 
725
733
  The specifics of this pipeline are subject to change between Qiskit versions. The broad principles
726
- are described below.
734
+ are described below. First, consider the more common case that the basis set is not of the form
735
+ Clifford+T.
727
736
 
728
737
  At optimization level 0, the stage is empty.
729
738
 
@@ -740,6 +749,8 @@ The optimization loop condition also tries multiple runs and chooses the minimum
740
749
  of fluctuating output; this is necessary because matrix-based resynthesis is relatively unstable in
741
750
  terms of concrete gates.
742
751
 
752
+ For a Clifford+T basis set, two-qubit matrix based resynthesis is not applied.
753
+
743
754
  Optimization level 3 is typically very expensive for large circuits.
744
755
 
745
756
 
@@ -74,30 +74,12 @@ class InstructionDurations:
74
74
 
75
75
  Raises:
76
76
  TranspilerError: If dt and dtm is different in the backend.
77
+ TypeError: If the backend is the wrong type
77
78
  """
78
79
  # All durations in seconds in gate_length
79
80
  if isinstance(backend, BackendV2):
80
81
  return backend.target.durations()
81
-
82
- instruction_durations = []
83
- backend_properties = backend.properties()
84
- if hasattr(backend_properties, "_gates"):
85
- for gate, insts in backend_properties._gates.items():
86
- for qubits, props in insts.items():
87
- if "gate_length" in props:
88
- gate_length = props["gate_length"][0] # Throw away datetime at index 1
89
- instruction_durations.append((gate, qubits, gate_length, "s"))
90
- for q, props in backend.properties()._qubits.items():
91
- if "readout_length" in props:
92
- readout_length = props["readout_length"][0] # Throw away datetime at index 1
93
- instruction_durations.append(("measure", [q], readout_length, "s"))
94
-
95
- try:
96
- dt = backend.configuration().dt
97
- except AttributeError:
98
- dt = None
99
-
100
- return cls(instruction_durations, dt=dt)
82
+ raise TypeError("Unsupported backend type: {backend}")
101
83
 
102
84
  def update(self, inst_durations: "InstructionDurationsType" | None, dt: float = None):
103
85
  """Update self with inst_durations (inst_durations overwrite self).
@@ -91,7 +91,7 @@ Optimizations
91
91
  Split2QUnitaries
92
92
  RemoveIdentityEquivalent
93
93
  ContractIdleWiresInControlFlow
94
- LightCone
94
+ OptimizeCliffordT
95
95
 
96
96
  Scheduling
97
97
  =============
@@ -102,6 +102,7 @@ Scheduling
102
102
  TimeUnitConversion
103
103
  ALAPScheduleAnalysis
104
104
  ASAPScheduleAnalysis
105
+ ContextAwareDynamicalDecoupling
105
106
  PadDynamicalDecoupling
106
107
  PadDelay
107
108
  ConstrainedReschedule
@@ -226,7 +227,7 @@ from .optimization import OptimizeAnnotated
226
227
  from .optimization import RemoveIdentityEquivalent
227
228
  from .optimization import Split2QUnitaries
228
229
  from .optimization import ContractIdleWiresInControlFlow
229
- from .optimization import LightCone
230
+ from .optimization import OptimizeCliffordT
230
231
 
231
232
  # circuit analysis
232
233
  from .analysis import ResourceEstimation
@@ -246,6 +247,7 @@ from .synthesis import HighLevelSynthesis
246
247
  from .synthesis import HLSConfig
247
248
  from .synthesis import SolovayKitaev
248
249
  from .synthesis import SolovayKitaevSynthesis
250
+ from .synthesis import CliffordUnitarySynthesis
249
251
  from .synthesis import AQCSynthesisPlugin
250
252
 
251
253
  # circuit scheduling
@@ -257,6 +259,7 @@ from .scheduling import PadDelay
257
259
  from .scheduling import ConstrainedReschedule
258
260
  from .scheduling import InstructionDurationCheck
259
261
  from .scheduling import SetIOLatency
262
+ from .scheduling import ContextAwareDynamicalDecoupling
260
263
 
261
264
  # additional utility passes
262
265
  from .utils import CheckMap
@@ -48,11 +48,11 @@ class ApplyLayout(TransformationPass):
48
48
  TranspilerError: if no layout is found in ``property_set`` or no full physical qubits.
49
49
  """
50
50
  layout = self.property_set["layout"]
51
- if not layout:
51
+ if layout is None:
52
52
  raise TranspilerError(
53
53
  "No 'layout' is found in property_set. Please run a Layout pass in advance."
54
54
  )
55
- if len(layout) != (1 + max(layout.get_physical_bits())):
55
+ if len(layout) != (1 + max(layout.get_physical_bits(), default=-1)):
56
56
  raise TranspilerError("The 'layout' must be full (with ancilla).")
57
57
 
58
58
  post_layout = self.property_set["post_layout"]
@@ -19,7 +19,8 @@ import rustworkx
19
19
  from qiskit.transpiler.layout import Layout
20
20
  from qiskit.transpiler.basepasses import AnalysisPass
21
21
  from qiskit.transpiler.exceptions import TranspilerError
22
- from qiskit.transpiler.passes.layout import disjoint_utils
22
+ from qiskit.transpiler.target import Target
23
+ from qiskit._accelerate import disjoint_utils
23
24
 
24
25
  from qiskit._accelerate.dense_layout import best_subset
25
26
 
@@ -66,11 +67,30 @@ class DenseLayout(AnalysisPass):
66
67
  raise TranspilerError(
67
68
  "A coupling_map or target with constrained qargs is necessary to run the pass."
68
69
  )
69
- layout_components = disjoint_utils.run_pass_over_connected_components(
70
- dag,
71
- self.coupling_map if self.target is None else self.target,
72
- self._inner_run,
73
- )
70
+ if self.target is not None:
71
+ layout_components = disjoint_utils.run_pass_over_connected_components(
72
+ dag,
73
+ self.target,
74
+ self._inner_run,
75
+ )
76
+ if layout_components is None:
77
+ target = Target.from_configuration(
78
+ basis_gates=["u", "cx"], coupling_map=self.coupling_map
79
+ )
80
+ layout_components = disjoint_utils.run_pass_over_connected_components(
81
+ dag,
82
+ target,
83
+ self._inner_run,
84
+ )
85
+ else:
86
+ target = Target.from_configuration(
87
+ basis_gates=["u", "cx"], coupling_map=self.coupling_map
88
+ )
89
+ layout_components = disjoint_utils.run_pass_over_connected_components(
90
+ dag,
91
+ target,
92
+ self._inner_run,
93
+ )
74
94
  layout_mapping = {}
75
95
  for component in layout_components:
76
96
  layout_mapping.update(component)
@@ -12,143 +12,12 @@
12
12
 
13
13
  """This module contains common utils for disjoint coupling maps."""
14
14
  from __future__ import annotations
15
- from collections import defaultdict
16
- from typing import List, Callable, TypeVar, Dict, Union
17
- import uuid
15
+ from typing import Union
18
16
 
19
- import rustworkx as rx
20
- from qiskit.dagcircuit import DAGOpNode
21
-
22
- from qiskit.circuit import Qubit, Barrier, Clbit
23
17
  from qiskit.dagcircuit.dagcircuit import DAGCircuit
24
- from qiskit.dagcircuit.dagnode import DAGOutNode
25
18
  from qiskit.transpiler.coupling import CouplingMap
26
19
  from qiskit.transpiler.target import Target
27
20
  from qiskit.transpiler.exceptions import TranspilerError
28
- from qiskit.transpiler.passes.layout import vf2_utils
29
-
30
- T = TypeVar("T")
31
-
32
-
33
- def run_pass_over_connected_components(
34
- dag: DAGCircuit,
35
- components_source: Union[Target, CouplingMap],
36
- run_func: Callable[[DAGCircuit, CouplingMap], T],
37
- ) -> List[T]:
38
- """Run a transpiler pass inner function over mapped components."""
39
- if isinstance(components_source, Target):
40
- coupling_map = components_source.build_coupling_map(filter_idle_qubits=True)
41
- else:
42
- coupling_map = components_source
43
- cmap_components = coupling_map.connected_components()
44
- # If graph is connected we only need to run the pass once
45
- if len(cmap_components) == 1:
46
- if dag.num_qubits() > cmap_components[0].size():
47
- raise TranspilerError(
48
- "A connected component of the DAGCircuit is too large for any of the connected "
49
- "components in the coupling map."
50
- )
51
- return [run_func(dag, cmap_components[0])]
52
- dag_components = separate_dag(dag)
53
- mapped_components = map_components(dag_components, cmap_components)
54
- out_component_pairs = []
55
- for cmap_index, dags in mapped_components.items():
56
- # Take the first dag from the mapped dag components and then
57
- # compose it with any other dag components that are operating on the
58
- # same coupling map connected component. This results in a subcircuit
59
- # of possibly disjoint circuit components which we will run the layout
60
- # pass on.
61
- out_dag = dag_components[dags.pop()]
62
- for dag_index in dags:
63
- dag = dag_components[dag_index]
64
- out_dag.add_qubits(dag.qubits)
65
- out_dag.add_clbits(dag.clbits)
66
- for qreg in dag.qregs:
67
- out_dag.add_qreg(qreg)
68
- for creg in dag.cregs:
69
- out_dag.add_creg(creg)
70
- out_dag.compose(dag, qubits=dag.qubits, clbits=dag.clbits)
71
- out_component_pairs.append((out_dag, cmap_components[cmap_index]))
72
- res = [run_func(out_dag, cmap) for out_dag, cmap in out_component_pairs]
73
- return res
74
-
75
-
76
- def map_components(
77
- dag_components: List[DAGCircuit], cmap_components: List[CouplingMap]
78
- ) -> Dict[int, List[int]]:
79
- """Returns a map where the key is the index of each connected component in cmap_components and
80
- the value is a list of indices from dag_components which should be placed onto it."""
81
- free_qubits = {index: len(cmap.graph) for index, cmap in enumerate(cmap_components)}
82
- out_mapping = defaultdict(list)
83
-
84
- for dag_index, dag in sorted(
85
- enumerate(dag_components), key=lambda x: x[1].num_qubits(), reverse=True
86
- ):
87
- for cmap_index in sorted(
88
- range(len(cmap_components)), key=lambda index: free_qubits[index], reverse=True
89
- ):
90
- # TODO: Improve heuristic to involve connectivity and estimate
91
- # swap cost
92
- if dag.num_qubits() <= free_qubits[cmap_index]:
93
- out_mapping[cmap_index].append(dag_index)
94
- free_qubits[cmap_index] -= dag.num_qubits()
95
- break
96
- else:
97
- raise TranspilerError(
98
- "A connected component of the DAGCircuit is too large for any of the connected "
99
- "components in the coupling map."
100
- )
101
- return out_mapping
102
-
103
-
104
- def split_barriers(dag: DAGCircuit):
105
- """Mutate an input dag to split barriers into single qubit barriers."""
106
- for node in dag.op_nodes(Barrier):
107
- num_qubits = len(node.qargs)
108
- if num_qubits == 1:
109
- continue
110
- if node.label:
111
- barrier_uuid = f"{node.op.label}_uuid={uuid.uuid4()}"
112
- else:
113
- barrier_uuid = f"_none_uuid={uuid.uuid4()}"
114
- split_dag = DAGCircuit()
115
- split_dag.add_qubits([Qubit() for _ in range(num_qubits)])
116
- for i in range(num_qubits):
117
- split_dag.apply_operation_back(
118
- Barrier(1, label=barrier_uuid),
119
- qargs=(split_dag.qubits[i],),
120
- check=False,
121
- )
122
- dag.substitute_node_with_dag(node, split_dag)
123
-
124
-
125
- def combine_barriers(dag: DAGCircuit, retain_uuid: bool = True):
126
- """Mutate input dag to combine barriers with UUID labels into a single barrier."""
127
- qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
128
- uuid_map: dict[str, DAGOpNode] = {}
129
- for node in dag.op_nodes(Barrier):
130
- if node.label:
131
- if "_uuid=" in node.label:
132
- barrier_uuid = node.label
133
- else:
134
- continue
135
- if barrier_uuid in uuid_map:
136
- other_node = uuid_map[barrier_uuid]
137
- num_qubits = len(other_node.qargs) + len(node.qargs)
138
- if not retain_uuid:
139
- if isinstance(node.label, str) and node.label.startswith("_none_uuid="):
140
- label = None
141
- elif isinstance(node.label, str) and "_uuid=" in node.label:
142
- label = "_uuid=".join(node.label.split("_uuid=")[:-1])
143
- else:
144
- label = barrier_uuid
145
- else:
146
- label = barrier_uuid
147
- new_op = Barrier(num_qubits, label=label)
148
- new_node = dag.replace_block_with_op([node, other_node], new_op, qubit_indices)
149
- uuid_map[barrier_uuid] = new_node
150
- else:
151
- uuid_map[barrier_uuid] = node
152
21
 
153
22
 
154
23
  def require_layout_isolated_to_component(
@@ -183,37 +52,3 @@ def require_layout_isolated_to_component(
183
52
  f"{dag.find_bit(inst.qargs[1]).index} needs to interact with the "
184
53
  f"qubit {dag.find_bit(inst.qargs[0]).index} and they belong to different components"
185
54
  )
186
-
187
-
188
- def separate_dag(dag: DAGCircuit) -> List[DAGCircuit]:
189
- """Separate a dag circuit into it's connected components."""
190
- # Split barriers into single qubit barriers before splitting connected components
191
- split_barriers(dag)
192
- im_graph, _, qubit_map, __ = vf2_utils.build_interaction_graph(dag)
193
- connected_components = rx.weakly_connected_components(im_graph)
194
- component_qubits = []
195
- for component in connected_components:
196
- component_qubits.append({qubit_map[x] for x in component})
197
-
198
- qubits = set(dag.qubits)
199
-
200
- decomposed_dags = []
201
- for dag_qubits in component_qubits:
202
- new_dag = dag.copy_empty_like()
203
- new_dag.remove_qubits(*qubits - dag_qubits)
204
- new_dag.global_phase = 0
205
- for node in dag.topological_op_nodes():
206
- if dag_qubits.issuperset(node.qargs):
207
- new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
208
- idle_clbits = []
209
- for bit, node in new_dag.input_map.items():
210
- succ_node = next(new_dag.successors(node))
211
- if isinstance(succ_node, DAGOutNode) and isinstance(succ_node.wire, Clbit):
212
- idle_clbits.append(bit)
213
- new_dag.remove_clbits(*idle_clbits)
214
- combine_barriers(new_dag)
215
- decomposed_dags.append(new_dag)
216
- # Reverse split barriers on input dag to avoid leaking out internal transformations as
217
- # part of splitting
218
- combine_barriers(dag, retain_uuid=False)
219
- return decomposed_dags