qiskit 1.0.2__cp38-abi3-win32.whl → 1.1.0rc1__cp38-abi3-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +27 -16
  3. qiskit/_accelerate.pyd +0 -0
  4. qiskit/_numpy_compat.py +73 -0
  5. qiskit/assembler/disassemble.py +5 -6
  6. qiskit/circuit/__init__.py +1131 -169
  7. qiskit/circuit/_classical_resource_map.py +7 -6
  8. qiskit/circuit/_utils.py +18 -8
  9. qiskit/circuit/annotated_operation.py +21 -0
  10. qiskit/circuit/barrier.py +10 -13
  11. qiskit/circuit/bit.py +0 -1
  12. qiskit/circuit/classical/__init__.py +2 -2
  13. qiskit/circuit/classical/expr/__init__.py +39 -5
  14. qiskit/circuit/classical/expr/constructors.py +84 -1
  15. qiskit/circuit/classical/expr/expr.py +83 -13
  16. qiskit/circuit/classical/expr/visitors.py +83 -0
  17. qiskit/circuit/commutation_checker.py +86 -51
  18. qiskit/circuit/controlflow/_builder_utils.py +9 -1
  19. qiskit/circuit/controlflow/break_loop.py +8 -22
  20. qiskit/circuit/controlflow/builder.py +116 -1
  21. qiskit/circuit/controlflow/continue_loop.py +8 -22
  22. qiskit/circuit/controlflow/control_flow.py +47 -8
  23. qiskit/circuit/controlflow/for_loop.py +8 -23
  24. qiskit/circuit/controlflow/if_else.py +13 -27
  25. qiskit/circuit/controlflow/switch_case.py +14 -21
  26. qiskit/circuit/controlflow/while_loop.py +9 -23
  27. qiskit/circuit/controlledgate.py +2 -2
  28. qiskit/circuit/delay.py +7 -5
  29. qiskit/circuit/gate.py +20 -7
  30. qiskit/circuit/instruction.py +31 -30
  31. qiskit/circuit/instructionset.py +9 -22
  32. qiskit/circuit/library/__init__.py +8 -2
  33. qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
  34. qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
  35. qiskit/circuit/library/blueprintcircuit.py +29 -7
  36. qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
  37. qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
  38. qiskit/circuit/library/generalized_gates/isometry.py +51 -254
  39. qiskit/circuit/library/generalized_gates/pauli.py +2 -2
  40. qiskit/circuit/library/generalized_gates/permutation.py +4 -1
  41. qiskit/circuit/library/generalized_gates/rv.py +15 -11
  42. qiskit/circuit/library/generalized_gates/uc.py +2 -98
  43. qiskit/circuit/library/generalized_gates/unitary.py +9 -4
  44. qiskit/circuit/library/hamiltonian_gate.py +11 -5
  45. qiskit/circuit/library/n_local/efficient_su2.py +5 -5
  46. qiskit/circuit/library/n_local/n_local.py +100 -49
  47. qiskit/circuit/library/n_local/two_local.py +3 -59
  48. qiskit/circuit/library/overlap.py +3 -3
  49. qiskit/circuit/library/phase_oracle.py +1 -1
  50. qiskit/circuit/library/quantum_volume.py +39 -38
  51. qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
  52. qiskit/circuit/library/standard_gates/global_phase.py +4 -2
  53. qiskit/circuit/library/standard_gates/i.py +1 -2
  54. qiskit/circuit/library/standard_gates/iswap.py +1 -2
  55. qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
  56. qiskit/circuit/library/standard_gates/p.py +31 -15
  57. qiskit/circuit/library/standard_gates/r.py +4 -3
  58. qiskit/circuit/library/standard_gates/rx.py +7 -4
  59. qiskit/circuit/library/standard_gates/rxx.py +4 -3
  60. qiskit/circuit/library/standard_gates/ry.py +7 -4
  61. qiskit/circuit/library/standard_gates/ryy.py +4 -3
  62. qiskit/circuit/library/standard_gates/rz.py +7 -4
  63. qiskit/circuit/library/standard_gates/rzx.py +4 -3
  64. qiskit/circuit/library/standard_gates/rzz.py +4 -3
  65. qiskit/circuit/library/standard_gates/s.py +4 -8
  66. qiskit/circuit/library/standard_gates/t.py +2 -4
  67. qiskit/circuit/library/standard_gates/u.py +16 -11
  68. qiskit/circuit/library/standard_gates/u1.py +6 -2
  69. qiskit/circuit/library/standard_gates/u2.py +4 -2
  70. qiskit/circuit/library/standard_gates/u3.py +9 -5
  71. qiskit/circuit/library/standard_gates/x.py +22 -11
  72. qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
  73. qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
  74. qiskit/circuit/library/standard_gates/z.py +1 -2
  75. qiskit/circuit/measure.py +4 -1
  76. qiskit/circuit/operation.py +13 -8
  77. qiskit/circuit/parameter.py +11 -6
  78. qiskit/circuit/quantumcircuit.py +864 -128
  79. qiskit/circuit/quantumcircuitdata.py +2 -2
  80. qiskit/circuit/reset.py +5 -2
  81. qiskit/circuit/store.py +95 -0
  82. qiskit/compiler/assembler.py +22 -22
  83. qiskit/compiler/transpiler.py +63 -112
  84. qiskit/converters/circuit_to_dag.py +7 -0
  85. qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
  86. qiskit/converters/circuit_to_gate.py +2 -0
  87. qiskit/converters/circuit_to_instruction.py +22 -0
  88. qiskit/converters/dag_to_circuit.py +4 -0
  89. qiskit/converters/dag_to_dagdependency_v2.py +44 -0
  90. qiskit/dagcircuit/collect_blocks.py +15 -10
  91. qiskit/dagcircuit/dagcircuit.py +434 -124
  92. qiskit/dagcircuit/dagdependency.py +19 -12
  93. qiskit/dagcircuit/dagdependency_v2.py +641 -0
  94. qiskit/dagcircuit/dagdepnode.py +19 -16
  95. qiskit/dagcircuit/dagnode.py +14 -4
  96. qiskit/primitives/__init__.py +12 -8
  97. qiskit/primitives/backend_estimator.py +3 -5
  98. qiskit/primitives/backend_estimator_v2.py +410 -0
  99. qiskit/primitives/backend_sampler_v2.py +287 -0
  100. qiskit/primitives/base/base_estimator.py +4 -9
  101. qiskit/primitives/base/base_sampler.py +2 -2
  102. qiskit/primitives/containers/__init__.py +5 -4
  103. qiskit/primitives/containers/bit_array.py +292 -2
  104. qiskit/primitives/containers/data_bin.py +123 -50
  105. qiskit/primitives/containers/estimator_pub.py +10 -3
  106. qiskit/primitives/containers/observables_array.py +2 -2
  107. qiskit/primitives/containers/pub_result.py +1 -1
  108. qiskit/primitives/containers/sampler_pub.py +19 -3
  109. qiskit/primitives/containers/sampler_pub_result.py +74 -0
  110. qiskit/primitives/containers/shape.py +1 -1
  111. qiskit/primitives/statevector_estimator.py +4 -4
  112. qiskit/primitives/statevector_sampler.py +7 -12
  113. qiskit/providers/__init__.py +17 -18
  114. qiskit/providers/backend.py +2 -2
  115. qiskit/providers/backend_compat.py +8 -10
  116. qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
  117. qiskit/providers/basic_provider/basic_simulator.py +81 -21
  118. qiskit/providers/fake_provider/fake_1q.py +1 -1
  119. qiskit/providers/fake_provider/fake_backend.py +3 -408
  120. qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
  121. qiskit/providers/provider.py +16 -0
  122. qiskit/pulse/builder.py +4 -1
  123. qiskit/pulse/parameter_manager.py +60 -4
  124. qiskit/pulse/schedule.py +29 -13
  125. qiskit/pulse/utils.py +61 -20
  126. qiskit/qasm2/__init__.py +1 -5
  127. qiskit/qasm2/parse.py +1 -4
  128. qiskit/qasm3/__init__.py +42 -5
  129. qiskit/qasm3/ast.py +19 -0
  130. qiskit/qasm3/exporter.py +178 -106
  131. qiskit/qasm3/printer.py +27 -5
  132. qiskit/qpy/__init__.py +247 -13
  133. qiskit/qpy/binary_io/circuits.py +216 -47
  134. qiskit/qpy/binary_io/schedules.py +42 -36
  135. qiskit/qpy/binary_io/value.py +201 -22
  136. qiskit/qpy/common.py +1 -1
  137. qiskit/qpy/exceptions.py +20 -0
  138. qiskit/qpy/formats.py +29 -0
  139. qiskit/qpy/type_keys.py +21 -0
  140. qiskit/quantum_info/analysis/distance.py +3 -3
  141. qiskit/quantum_info/analysis/make_observable.py +2 -1
  142. qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
  143. qiskit/quantum_info/operators/channel/chi.py +9 -8
  144. qiskit/quantum_info/operators/channel/choi.py +10 -9
  145. qiskit/quantum_info/operators/channel/kraus.py +2 -1
  146. qiskit/quantum_info/operators/channel/ptm.py +10 -9
  147. qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
  148. qiskit/quantum_info/operators/channel/stinespring.py +2 -1
  149. qiskit/quantum_info/operators/channel/superop.py +12 -11
  150. qiskit/quantum_info/operators/channel/transformations.py +12 -11
  151. qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
  152. qiskit/quantum_info/operators/operator.py +43 -30
  153. qiskit/quantum_info/operators/scalar_op.py +10 -9
  154. qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
  155. qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
  156. qiskit/quantum_info/operators/symplectic/pauli.py +48 -4
  157. qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
  158. qiskit/quantum_info/operators/symplectic/random.py +3 -2
  159. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +54 -33
  160. qiskit/quantum_info/states/densitymatrix.py +13 -13
  161. qiskit/quantum_info/states/stabilizerstate.py +3 -3
  162. qiskit/quantum_info/states/statevector.py +14 -13
  163. qiskit/quantum_info/states/utils.py +5 -3
  164. qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
  165. qiskit/result/mitigation/local_readout_mitigator.py +2 -1
  166. qiskit/result/mitigation/utils.py +3 -2
  167. qiskit/synthesis/__init__.py +2 -0
  168. qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
  169. qiskit/synthesis/evolution/lie_trotter.py +7 -14
  170. qiskit/synthesis/evolution/qdrift.py +3 -4
  171. qiskit/synthesis/linear/cnot_synth.py +1 -3
  172. qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
  173. qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
  174. qiskit/synthesis/permutation/__init__.py +1 -0
  175. qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
  176. qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
  177. qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
  178. qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
  179. qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
  180. qiskit/synthesis/unitary/aqc/__init__.py +1 -1
  181. qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
  182. qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
  183. qiskit/synthesis/unitary/qsd.py +3 -2
  184. qiskit/transpiler/__init__.py +7 -3
  185. qiskit/transpiler/layout.py +140 -61
  186. qiskit/transpiler/passes/__init__.py +6 -0
  187. qiskit/transpiler/passes/basis/basis_translator.py +7 -2
  188. qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
  189. qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
  190. qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
  191. qiskit/transpiler/passes/layout/apply_layout.py +8 -3
  192. qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
  193. qiskit/transpiler/passes/layout/set_layout.py +1 -1
  194. qiskit/transpiler/passes/optimization/__init__.py +2 -0
  195. qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
  196. qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
  197. qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
  198. qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
  199. qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
  200. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
  201. qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
  202. qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
  203. qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
  204. qiskit/transpiler/passes/routing/__init__.py +1 -0
  205. qiskit/transpiler/passes/routing/basic_swap.py +13 -2
  206. qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
  207. qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
  208. qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
  209. qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
  210. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  211. qiskit/transpiler/passes/scheduling/alap.py +1 -2
  212. qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
  213. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
  214. qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
  215. qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
  216. qiskit/transpiler/passes/scheduling/asap.py +1 -2
  217. qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
  218. qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
  219. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
  220. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
  221. qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
  222. qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
  223. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
  224. qiskit/transpiler/passes/utils/gates_basis.py +3 -3
  225. qiskit/transpiler/passmanager.py +44 -1
  226. qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
  227. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
  228. qiskit/transpiler/preset_passmanagers/common.py +4 -6
  229. qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
  230. qiskit/utils/optionals.py +6 -2
  231. qiskit/visualization/array.py +1 -1
  232. qiskit/visualization/bloch.py +2 -3
  233. qiskit/visualization/circuit/matplotlib.py +44 -14
  234. qiskit/visualization/circuit/text.py +38 -18
  235. qiskit/visualization/counts_visualization.py +3 -6
  236. qiskit/visualization/dag_visualization.py +6 -7
  237. qiskit/visualization/pulse_v2/interface.py +8 -3
  238. qiskit/visualization/state_visualization.py +3 -2
  239. qiskit/visualization/timeline/interface.py +18 -8
  240. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/METADATA +12 -8
  241. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/RECORD +245 -235
  242. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/WHEEL +1 -1
  243. qiskit/_qasm2.pyd +0 -0
  244. qiskit/_qasm3.pyd +0 -0
  245. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/LICENSE.txt +0 -0
  246. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/entry_points.txt +0 -0
  247. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,37 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2017, 2023.
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
+ """Remove reset when it is the final instruction on a qubit."""
14
+
15
+ from qiskit.circuit import Reset, Qubit
16
+ from qiskit.dagcircuit import DAGOpNode
17
+ from qiskit.transpiler.basepasses import TransformationPass
18
+
19
+
20
+ class RemoveFinalReset(TransformationPass):
21
+ """Remove reset when it is the final instruction on a qubit wire."""
22
+
23
+ def run(self, dag):
24
+ """Run the RemoveFinalReset pass on `dag`.
25
+
26
+ Args:
27
+ dag (DAGCircuit): the DAG to be optimized.
28
+
29
+ Returns:
30
+ DAGCircuit: the optimized DAG.
31
+ """
32
+ for output_node in dag.output_map.values():
33
+ if isinstance(output_node.wire, Qubit):
34
+ pred = next(dag.predecessors(output_node))
35
+ if isinstance(pred, DAGOpNode) and isinstance(pred.op, Reset):
36
+ dag.remove_op_node(pred)
37
+ return dag
@@ -148,9 +148,7 @@ class ForwardMatch:
148
148
 
149
149
  if self.template_dag_dep.direct_successors(node_id_t):
150
150
  maximal_index = self.template_dag_dep.direct_successors(node_id_t)[-1]
151
- for elem in pred:
152
- if elem > maximal_index:
153
- pred.remove(elem)
151
+ pred = [elem for elem in pred if elem <= maximal_index]
154
152
 
155
153
  block = []
156
154
  for node_id in pred:
@@ -19,3 +19,4 @@ from .stochastic_swap import StochasticSwap
19
19
  from .sabre_swap import SabreSwap
20
20
  from .commuting_2q_gate_routing.commuting_2q_gate_router import Commuting2qGateRouter
21
21
  from .commuting_2q_gate_routing.swap_strategy import SwapStrategy
22
+ from .star_prerouting import StarPreRouting
@@ -114,7 +114,13 @@ class BasicSwap(TransformationPass):
114
114
  order = current_layout.reorder_bits(new_dag.qubits)
115
115
  new_dag.compose(subdag, qubits=order)
116
116
 
117
- self.property_set["final_layout"] = current_layout
117
+ if self.property_set["final_layout"] is None:
118
+ self.property_set["final_layout"] = current_layout
119
+ else:
120
+ self.property_set["final_layout"] = current_layout.compose(
121
+ self.property_set["final_layout"], dag.qubits
122
+ )
123
+
118
124
  return new_dag
119
125
 
120
126
  def _fake_run(self, dag):
@@ -151,5 +157,10 @@ class BasicSwap(TransformationPass):
151
157
  for swap in range(len(path) - 2):
152
158
  current_layout.swap(path[swap], path[swap + 1])
153
159
 
154
- self.property_set["final_layout"] = current_layout
160
+ if self.property_set["final_layout"] is None:
161
+ self.property_set["final_layout"] = current_layout
162
+ else:
163
+ self.property_set["final_layout"] = current_layout.compose(
164
+ self.property_set["final_layout"], dag.qubits
165
+ )
155
166
  return dag
@@ -169,7 +169,13 @@ class LookaheadSwap(TransformationPass):
169
169
 
170
170
  mapped_gates.extend(gates_mapped)
171
171
 
172
- self.property_set["final_layout"] = current_state.layout
172
+ if self.property_set["final_layout"] is None:
173
+ self.property_set["final_layout"] = current_state.layout
174
+ else:
175
+ self.property_set["final_layout"] = current_state.layout.compose(
176
+ self.property_set["final_layout"], dag.qubits
177
+ )
178
+
173
179
  if self.fake_run:
174
180
  return dag
175
181
 
@@ -31,8 +31,8 @@ from qiskit.transpiler.passes.layout import disjoint_utils
31
31
  from qiskit.dagcircuit import DAGCircuit
32
32
  from qiskit.utils.parallel import CPU_COUNT
33
33
 
34
- from qiskit._accelerate.sabre_swap import (
35
- build_swap_map,
34
+ from qiskit._accelerate.sabre import (
35
+ sabre_routing,
36
36
  Heuristic,
37
37
  NeighborTable,
38
38
  SabreDAG,
@@ -239,8 +239,7 @@ class SabreSwap(TransformationPass):
239
239
  self._qubit_indices,
240
240
  )
241
241
  sabre_start = time.perf_counter()
242
- *sabre_result, final_permutation = build_swap_map(
243
- len(dag.qubits),
242
+ *sabre_result, final_permutation = sabre_routing(
244
243
  sabre_dag,
245
244
  self._neighbor_table,
246
245
  self.dist_matrix,
@@ -251,8 +250,13 @@ class SabreSwap(TransformationPass):
251
250
  )
252
251
  sabre_stop = time.perf_counter()
253
252
  logging.debug("Sabre swap algorithm execution complete in: %s", sabre_stop - sabre_start)
254
-
255
- self.property_set["final_layout"] = Layout(dict(zip(dag.qubits, final_permutation)))
253
+ final_layout = Layout(dict(zip(dag.qubits, final_permutation)))
254
+ if self.property_set["final_layout"] is None:
255
+ self.property_set["final_layout"] = final_layout
256
+ else:
257
+ self.property_set["final_layout"] = final_layout.compose(
258
+ self.property_set["final_layout"], dag.qubits
259
+ )
256
260
  if self.fake_run:
257
261
  return dag
258
262
  return _apply_sabre_result(
@@ -0,0 +1,417 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2023.
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
+ """Search for star connectivity patterns and replace them with."""
14
+ from typing import Iterable, Union, Optional, List, Tuple
15
+ from math import floor, log10
16
+
17
+ from qiskit.circuit import Barrier
18
+ from qiskit.dagcircuit import DAGOpNode, DAGDepNode, DAGDependency, DAGCircuit
19
+ from qiskit.transpiler import Layout
20
+ from qiskit.transpiler.basepasses import TransformationPass
21
+ from qiskit.circuit.library import SwapGate
22
+
23
+
24
+ class StarBlock:
25
+ """Defines blocks representing star-shaped pieces of a circuit."""
26
+
27
+ def __init__(self, nodes=None, center=None, num2q=0):
28
+ self.center = center
29
+ self.num2q = num2q
30
+ self.nodes = [] if nodes is None else nodes
31
+
32
+ def get_nodes(self):
33
+ """Returns the list of nodes used in the block."""
34
+ return self.nodes
35
+
36
+ def append_node(self, node):
37
+ """
38
+ If node can be added to block while keeping the block star-shaped, and
39
+ return True. Otherwise, does not add node to block and returns False.
40
+ """
41
+
42
+ added = False
43
+
44
+ if len(node.qargs) == 1:
45
+ self.nodes.append(node)
46
+ added = True
47
+ elif self.center is None:
48
+ self.center = set(node.qargs)
49
+ self.nodes.append(node)
50
+ self.num2q += 1
51
+ added = True
52
+ elif isinstance(self.center, set):
53
+ if node.qargs[0] in self.center:
54
+ self.center = node.qargs[0]
55
+ self.nodes.append(node)
56
+ self.num2q += 1
57
+ added = True
58
+ elif node.qargs[1] in self.center:
59
+ self.center = node.qargs[1]
60
+ self.nodes.append(node)
61
+ self.num2q += 1
62
+ added = True
63
+ else:
64
+ if self.center in node.qargs:
65
+ self.nodes.append(node)
66
+ self.num2q += 1
67
+ added = True
68
+
69
+ return added
70
+
71
+ def size(self):
72
+ """
73
+ Returns the number of two-qubit quantum gates in this block.
74
+ """
75
+ return self.num2q
76
+
77
+
78
+ class StarPreRouting(TransformationPass):
79
+ """Run star to linear pre-routing
80
+
81
+ This pass is a logical optimization pass that rewrites any
82
+ solely 2q gate star connectivity subcircuit as a linear connectivity
83
+ equivalent with swaps.
84
+
85
+ For example:
86
+
87
+ .. plot::
88
+ :include-source:
89
+
90
+ from qiskit.circuit import QuantumCircuit
91
+ from qiskit.transpiler.passes import StarPreRouting
92
+
93
+ qc = QuantumCircuit(10)
94
+ qc.h(0)
95
+ qc.cx(0, range(1, 5))
96
+ qc.h(9)
97
+ qc.cx(9, range(8, 4, -1))
98
+ qc.measure_all()
99
+ StarPreRouting()(qc).draw("mpl")
100
+
101
+ This pass was inspired by a similar pass described in Section IV of:
102
+ C. Campbell et al., "Superstaq: Deep Optimization of Quantum Programs,"
103
+ 2023 IEEE International Conference on Quantum Computing and Engineering (QCE),
104
+ Bellevue, WA, USA, 2023, pp. 1020-1032, doi: 10.1109/QCE57702.2023.00116.
105
+ """
106
+
107
+ def __init__(self):
108
+ """StarPreRouting"""
109
+
110
+ self._pending_nodes: Optional[list[Union[DAGOpNode, DAGDepNode]]] = None
111
+ self._in_degree: Optional[dict[Union[DAGOpNode, DAGDepNode], int]] = None
112
+ super().__init__()
113
+
114
+ def _setup_in_degrees(self, dag):
115
+ """For an efficient implementation, for every node we keep the number of its
116
+ unprocessed immediate predecessors (called ``_in_degree``). This ``_in_degree``
117
+ is set up at the start and updated throughout the algorithm.
118
+ A node is leaf (or input) node iff its ``_in_degree`` is 0.
119
+ When a node is (marked as) collected, the ``_in_degree`` of each of its immediate
120
+ successor is updated by subtracting 1.
121
+ Additionally, ``_pending_nodes`` explicitly keeps the list of nodes whose
122
+ ``_in_degree`` is 0.
123
+ """
124
+ self._pending_nodes = []
125
+ self._in_degree = {}
126
+ for node in self._op_nodes(dag):
127
+ deg = len(self._direct_preds(dag, node))
128
+ self._in_degree[node] = deg
129
+ if deg == 0:
130
+ self._pending_nodes.append(node)
131
+
132
+ def _op_nodes(self, dag) -> Iterable[Union[DAGOpNode, DAGDepNode]]:
133
+ """Returns DAG nodes."""
134
+ if not isinstance(dag, DAGDependency):
135
+ return dag.op_nodes()
136
+ else:
137
+ return dag.get_nodes()
138
+
139
+ def _direct_preds(self, dag, node):
140
+ """Returns direct predecessors of a node. This function takes into account the
141
+ direction of collecting blocks, that is node's predecessors when collecting
142
+ backwards are the direct successors of a node in the DAG.
143
+ """
144
+ if not isinstance(dag, DAGDependency):
145
+ return [pred for pred in dag.predecessors(node) if isinstance(pred, DAGOpNode)]
146
+ else:
147
+ return [dag.get_node(pred_id) for pred_id in dag.direct_predecessors(node.node_id)]
148
+
149
+ def _direct_succs(self, dag, node):
150
+ """Returns direct successors of a node. This function takes into account the
151
+ direction of collecting blocks, that is node's successors when collecting
152
+ backwards are the direct predecessors of a node in the DAG.
153
+ """
154
+ if not isinstance(dag, DAGDependency):
155
+ return [succ for succ in dag.successors(node) if isinstance(succ, DAGOpNode)]
156
+ else:
157
+ return [dag.get_node(succ_id) for succ_id in dag.direct_successors(node.node_id)]
158
+
159
+ def _have_uncollected_nodes(self):
160
+ """Returns whether there are uncollected (pending) nodes"""
161
+ return len(self._pending_nodes) > 0
162
+
163
+ def collect_matching_block(self, dag, filter_fn):
164
+ """Iteratively collects the largest block of input nodes (that is, nodes with
165
+ ``_in_degree`` equal to 0) that match a given filtering function.
166
+ Examples of this include collecting blocks of swap gates,
167
+ blocks of linear gates (CXs and SWAPs), blocks of Clifford gates, blocks of single-qubit gates,
168
+ blocks of two-qubit gates, etc. Here 'iteratively' means that once a node is collected,
169
+ the ``_in_degree`` of each of its immediate successor is decreased by 1, allowing more nodes
170
+ to become input and to be eligible for collecting into the current block.
171
+ Returns the block of collected nodes.
172
+ """
173
+ unprocessed_pending_nodes = self._pending_nodes
174
+ self._pending_nodes = []
175
+
176
+ current_block = StarBlock()
177
+
178
+ # Iteratively process unprocessed_pending_nodes:
179
+ # - any node that does not match filter_fn is added to pending_nodes
180
+ # - any node that match filter_fn is added to the current_block,
181
+ # and some of its successors may be moved to unprocessed_pending_nodes.
182
+ while unprocessed_pending_nodes:
183
+ new_pending_nodes = []
184
+ for node in unprocessed_pending_nodes:
185
+ added = filter_fn(node) and current_block.append_node(node)
186
+ if added:
187
+ # update the _in_degree of node's successors
188
+ for suc in self._direct_succs(dag, node):
189
+ self._in_degree[suc] -= 1
190
+ if self._in_degree[suc] == 0:
191
+ new_pending_nodes.append(suc)
192
+ else:
193
+ self._pending_nodes.append(node)
194
+ unprocessed_pending_nodes = new_pending_nodes
195
+
196
+ return current_block
197
+
198
+ def collect_all_matching_blocks(
199
+ self,
200
+ dag,
201
+ min_block_size=2,
202
+ ):
203
+ """Collects all blocks that match a given filtering function filter_fn.
204
+ This iteratively finds the largest block that does not match filter_fn,
205
+ then the largest block that matches filter_fn, and so on, until no more uncollected
206
+ nodes remain. Intuitively, finding larger blocks of non-matching nodes helps to
207
+ find larger blocks of matching nodes later on. The option ``min_block_size``
208
+ specifies the minimum number of gates in the block for the block to be collected.
209
+
210
+ By default, blocks are collected in the direction from the inputs towards the outputs
211
+ of the circuit. The option ``collect_from_back`` allows to change this direction,
212
+ that is collect blocks from the outputs towards the inputs of the circuit.
213
+
214
+ Returns the list of matching blocks only.
215
+ """
216
+
217
+ def filter_fn(node):
218
+ """Specifies which nodes can be collected into star blocks."""
219
+ return (
220
+ len(node.qargs) <= 2
221
+ and len(node.cargs) == 0
222
+ and getattr(node.op, "condition", None) is None
223
+ and not isinstance(node.op, Barrier)
224
+ )
225
+
226
+ def not_filter_fn(node):
227
+ """Returns the opposite of filter_fn."""
228
+ return not filter_fn(node)
229
+
230
+ # Note: the collection direction must be specified before setting in-degrees
231
+ self._setup_in_degrees(dag)
232
+
233
+ # Iteratively collect non-matching and matching blocks.
234
+ matching_blocks: list[StarBlock] = []
235
+ processing_order = []
236
+ while self._have_uncollected_nodes():
237
+ self.collect_matching_block(dag, filter_fn=not_filter_fn)
238
+ matching_block = self.collect_matching_block(dag, filter_fn=filter_fn)
239
+ if matching_block.size() >= min_block_size:
240
+ matching_blocks.append(matching_block)
241
+ processing_order.append(matching_block)
242
+
243
+ processing_order = [n for p in processing_order for n in p.nodes]
244
+
245
+ return matching_blocks, processing_order
246
+
247
+ def run(self, dag):
248
+ # Extract StarBlocks from DAGCircuit / DAGDependency / DAGDependencyV2
249
+ star_blocks, processing_order = self.determine_star_blocks_processing(dag, min_block_size=2)
250
+
251
+ if not star_blocks:
252
+ return dag
253
+
254
+ if all(b.size() < 3 for b in star_blocks):
255
+ # we only process blocks with less than 3 two-qubit gates in this pre-routing pass
256
+ # if they occur in a collection of larger stars, otherwise we consider them to be 'lines'
257
+ return dag
258
+
259
+ # Create a new DAGCircuit / DAGDependency / DAGDependencyV2, replacing each
260
+ # star block by a linear sequence of gates
261
+ new_dag, qubit_mapping = self.star_preroute(dag, star_blocks, processing_order)
262
+
263
+ # Fix output permuation -- copied from ElidePermutations
264
+ input_qubit_mapping = {qubit: index for index, qubit in enumerate(dag.qubits)}
265
+ self.property_set["original_layout"] = Layout(input_qubit_mapping)
266
+ if self.property_set["original_qubit_indices"] is None:
267
+ self.property_set["original_qubit_indices"] = input_qubit_mapping
268
+
269
+ new_layout = Layout({dag.qubits[out]: idx for idx, out in enumerate(qubit_mapping)})
270
+ if current_layout := self.property_set["virtual_permutation_layout"]:
271
+ self.property_set["virtual_permutation_layout"] = new_layout.compose(
272
+ current_layout.inverse(dag.qubits, dag.qubits), dag.qubits
273
+ )
274
+ else:
275
+ self.property_set["virtual_permutation_layout"] = new_layout
276
+
277
+ return new_dag
278
+
279
+ def determine_star_blocks_processing(
280
+ self, dag: Union[DAGCircuit, DAGDependency], min_block_size: int
281
+ ) -> Tuple[List[StarBlock], Union[List[DAGOpNode], List[DAGDepNode]]]:
282
+ """Returns star blocks in dag and the processing order of nodes within these star blocks
283
+ Args:
284
+ dag (DAGCircuit or DAGDependency): a dag on which star blocks should be determined.
285
+ min_block_size (int): minimum number of two-qubit gates in a star block.
286
+
287
+ Returns:
288
+ List[StarBlock]: a list of star blocks in the given dag
289
+ Union[List[DAGOpNode], List[DAGDepNode]]: a list of operations specifying processing order
290
+ """
291
+ blocks, processing_order = self.collect_all_matching_blocks(
292
+ dag, min_block_size=min_block_size
293
+ )
294
+ return blocks, processing_order
295
+
296
+ def star_preroute(self, dag, blocks, processing_order):
297
+ """Returns star blocks in dag and the processing order of nodes within these star blocks
298
+ Args:
299
+ dag (DAGCircuit or DAGDependency): a dag on which star prerouting should be performed.
300
+ blocks (List[StarBlock]): a list of star blocks in the given dag.
301
+ processing_order (Union[List[DAGOpNode], List[DAGDepNode]]): a list of operations specifying
302
+ processing order
303
+
304
+ Returns:
305
+ new_dag: a dag specifying the pre-routed circuit
306
+ qubit_mapping: the final qubit mapping after pre-routing
307
+ """
308
+ node_to_block_id = {}
309
+ for i, block in enumerate(blocks):
310
+ for node in block.get_nodes():
311
+ node_to_block_id[node] = i
312
+
313
+ new_dag = dag.copy_empty_like()
314
+ processed_block_ids = set()
315
+ qubit_mapping = list(range(len(dag.qubits)))
316
+
317
+ def _apply_mapping(qargs, qubit_mapping, qubits):
318
+ return tuple(qubits[qubit_mapping[dag.find_bit(qubit).index]] for qubit in qargs)
319
+
320
+ is_first_star = True
321
+ last_2q_gate = [
322
+ op
323
+ for op in reversed(processing_order)
324
+ if ((len(op.qargs) > 1) and (op.name != "barrier"))
325
+ ]
326
+ if len(last_2q_gate) > 0:
327
+ last_2q_gate = last_2q_gate[0]
328
+ else:
329
+ last_2q_gate = None
330
+
331
+ int_digits = floor(log10(len(processing_order))) + 1
332
+ processing_order_s = set(processing_order)
333
+
334
+ def tie_breaker_key(node):
335
+ if node in processing_order_s:
336
+ return "a" + str(processing_order.index(node)).zfill(int(int_digits))
337
+ else:
338
+ return node.sort_key
339
+
340
+ for node in dag.topological_op_nodes(key=tie_breaker_key):
341
+ block_id = node_to_block_id.get(node, None)
342
+ if block_id is not None:
343
+ if block_id in processed_block_ids:
344
+ continue
345
+
346
+ processed_block_ids.add(block_id)
347
+
348
+ # process the whole block
349
+ block = blocks[block_id]
350
+ sequence = block.nodes
351
+ center_node = block.center
352
+
353
+ if len(sequence) == 2:
354
+ for inner_node in sequence:
355
+ new_dag.apply_operation_back(
356
+ inner_node.op,
357
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
358
+ inner_node.cargs,
359
+ check=False,
360
+ )
361
+ continue
362
+ swap_source = None
363
+ prev = None
364
+ for inner_node in sequence:
365
+ if (len(inner_node.qargs) == 1) or (inner_node.qargs == prev):
366
+ new_dag.apply_operation_back(
367
+ inner_node.op,
368
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
369
+ inner_node.cargs,
370
+ check=False,
371
+ )
372
+ continue
373
+ if is_first_star and swap_source is None:
374
+ swap_source = center_node
375
+ new_dag.apply_operation_back(
376
+ inner_node.op,
377
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
378
+ inner_node.cargs,
379
+ check=False,
380
+ )
381
+
382
+ prev = inner_node.qargs
383
+ continue
384
+ # place 2q-gate and subsequent swap gate
385
+ new_dag.apply_operation_back(
386
+ inner_node.op,
387
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
388
+ inner_node.cargs,
389
+ check=False,
390
+ )
391
+
392
+ if not inner_node is last_2q_gate and not isinstance(inner_node.op, Barrier):
393
+ new_dag.apply_operation_back(
394
+ SwapGate(),
395
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
396
+ inner_node.cargs,
397
+ check=False,
398
+ )
399
+ # Swap mapping
400
+ index_0 = dag.find_bit(inner_node.qargs[0]).index
401
+ index_1 = dag.find_bit(inner_node.qargs[1]).index
402
+ qubit_mapping[index_1], qubit_mapping[index_0] = (
403
+ qubit_mapping[index_0],
404
+ qubit_mapping[index_1],
405
+ )
406
+
407
+ prev = inner_node.qargs
408
+ is_first_star = False
409
+ else:
410
+ # the node is not part of a block
411
+ new_dag.apply_operation_back(
412
+ node.op,
413
+ _apply_mapping(node.qargs, qubit_mapping, dag.qubits),
414
+ node.cargs,
415
+ check=False,
416
+ )
417
+ return new_dag, qubit_mapping
@@ -33,7 +33,6 @@ from qiskit.circuit import (
33
33
  ForLoopOp,
34
34
  SwitchCaseOp,
35
35
  ControlFlowOp,
36
- Instruction,
37
36
  CASE_DEFAULT,
38
37
  )
39
38
  from qiskit._accelerate import stochastic_swap as stochastic_swap_rs
@@ -266,11 +265,15 @@ class StochasticSwap(TransformationPass):
266
265
  # Output any swaps
267
266
  if best_depth > 0:
268
267
  logger.debug("layer_update: there are swaps in this layer, depth %d", best_depth)
269
- dag.compose(best_circuit, qubits={bit: bit for bit in best_circuit.qubits})
268
+ dag.compose(
269
+ best_circuit, qubits={bit: bit for bit in best_circuit.qubits}, inline_captures=True
270
+ )
270
271
  else:
271
272
  logger.debug("layer_update: there are no swaps in this layer")
272
273
  # Output this layer
273
- dag.compose(layer["graph"], qubits=best_layout.reorder_bits(dag.qubits))
274
+ dag.compose(
275
+ layer["graph"], qubits=best_layout.reorder_bits(dag.qubits), inline_captures=True
276
+ )
274
277
 
275
278
  def _mapper(self, circuit_graph, coupling_graph, trials=20):
276
279
  """Map a DAGCircuit onto a CouplingMap using swap gates.
@@ -323,7 +326,7 @@ class StochasticSwap(TransformationPass):
323
326
  # Update the DAG
324
327
  if not self.fake_run:
325
328
  self._layer_update(
326
- dagcircuit_output, layerlist[i], best_layout, best_depth, best_circuit
329
+ dagcircuit_output, layer, best_layout, best_depth, best_circuit
327
330
  )
328
331
  continue
329
332
 
@@ -375,8 +378,13 @@ class StochasticSwap(TransformationPass):
375
378
  # any measurements that needed to be removed earlier.
376
379
  logger.debug("mapper: self.initial_layout = %s", self.initial_layout)
377
380
  logger.debug("mapper: layout = %s", layout)
381
+ if self.property_set["final_layout"] is None:
382
+ self.property_set["final_layout"] = layout
383
+ else:
384
+ self.property_set["final_layout"] = layout.compose(
385
+ self.property_set["final_layout"], circuit_graph.qubits
386
+ )
378
387
 
379
- self.property_set["final_layout"] = layout
380
388
  if self.fake_run:
381
389
  return circuit_graph
382
390
  return dagcircuit_output
@@ -433,7 +441,7 @@ class StochasticSwap(TransformationPass):
433
441
  root_dag, self.coupling_map, layout, final_layout, seed=self._new_seed()
434
442
  )
435
443
  if swap_dag.size(recurse=False):
436
- updated_dag_block.compose(swap_dag, qubits=swap_qubits)
444
+ updated_dag_block.compose(swap_dag, qubits=swap_qubits, inline_captures=True)
437
445
  idle_qubits &= set(updated_dag_block.idle_wires())
438
446
 
439
447
  # Now for each block, expand it to be full width over all active wires (all blocks of a
@@ -499,10 +507,18 @@ def _dag_from_block(block, node, root_dag):
499
507
  out.add_qreg(qreg)
500
508
  # For clbits, we need to take more care. Nested control-flow might need registers to exist for
501
509
  # conditions on inner blocks. `DAGCircuit.substitute_node_with_dag` handles this register
502
- # mapping when required, so we use that with a dummy block.
510
+ # mapping when required, so we use that with a dummy block that pretends to act on all variables
511
+ # in the DAG.
503
512
  out.add_clbits(node.cargs)
513
+ for var in block.iter_input_vars():
514
+ out.add_input_var(var)
515
+ for var in block.iter_captured_vars():
516
+ out.add_captured_var(var)
517
+ for var in block.iter_declared_vars():
518
+ out.add_declared_var(var)
519
+
504
520
  dummy = out.apply_operation_back(
505
- Instruction("dummy", len(node.qargs), len(node.cargs), []),
521
+ IfElseOp(expr.lift(True), block.copy_empty_like(vars_mode="captures")),
506
522
  node.qargs,
507
523
  node.cargs,
508
524
  check=False,
@@ -20,7 +20,7 @@ from .time_unit_conversion import TimeUnitConversion
20
20
  from .padding import PadDelay, PadDynamicalDecoupling
21
21
  from .alignments import InstructionDurationCheck, ValidatePulseGates, ConstrainedReschedule
22
22
 
23
- # For backward compability
23
+ # For backward compatibility
24
24
  from . import alignments as instruction_alignments
25
25
 
26
26
  # TODO Deprecated pass. Will be removed after deprecation period.
@@ -32,8 +32,7 @@ class ALAPSchedule(BaseSchedulerTransform):
32
32
  "Instead, use :class:`~.ALAPScheduleAnalysis`, which is an "
33
33
  "analysis pass that requires a padding pass to later modify the circuit."
34
34
  ),
35
- since="0.21.0",
36
- pending=True,
35
+ since="1.1.0",
37
36
  )
38
37
  def __init__(self, *args, **kwargs):
39
38
  super().__init__(*args, **kwargs)
@@ -95,8 +95,7 @@ class AlignMeasures(TransformationPass):
95
95
  "Instead, use :class:`~.ConstrainedReschedule`, which performs the same function "
96
96
  "but also supports aligning to additional timing constraints."
97
97
  ),
98
- since="0.21.0",
99
- pending=True,
98
+ since="1.1.0",
100
99
  )
101
100
  def __init__(self, alignment: int = 1):
102
101
  """Create new pass.