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
@@ -23,6 +23,7 @@ from qiskit.transpiler.layout import Layout
23
23
  from qiskit.transpiler.basepasses import AnalysisPass
24
24
  from qiskit.transpiler.exceptions import TranspilerError
25
25
  from qiskit.transpiler.passes.layout import vf2_utils
26
+ from qiskit._accelerate.vf2_layout import vf2_layout_pass, MultiQEncountered
26
27
 
27
28
 
28
29
  logger = logging.getLogger(__name__)
@@ -110,13 +111,7 @@ class VF2Layout(AnalysisPass):
110
111
  """
111
112
  super().__init__()
112
113
  self.target = target
113
- if (
114
- target is not None
115
- and (target_coupling_map := self.target.build_coupling_map()) is not None
116
- ):
117
- self.coupling_map = target_coupling_map
118
- else:
119
- self.coupling_map = coupling_map
114
+ self.coupling_map = coupling_map
120
115
  self.strict_direction = strict_direction
121
116
  self.seed = seed
122
117
  self.call_limit = call_limit
@@ -126,11 +121,52 @@ class VF2Layout(AnalysisPass):
126
121
 
127
122
  def run(self, dag):
128
123
  """run the layout method"""
129
- if self.coupling_map is None:
124
+ if self.target is None and self.coupling_map is None:
130
125
  raise TranspilerError("coupling_map or target must be specified.")
126
+ if self.coupling_map is None:
127
+ target, coupling_map = self.target, self.target.build_coupling_map()
128
+ elif self.target is None:
129
+ coupling_map = self.coupling_map
130
+ target = vf2_utils.build_dummy_target(coupling_map)
131
+ else:
132
+ # We have both, but may need to override the target if it has no connectivity.
133
+ coupling_map = self.target.build_coupling_map()
134
+ if coupling_map is None:
135
+ target = vf2_utils.build_dummy_target(self.coupling_map)
136
+ coupling_map = self.coupling_map
137
+ else:
138
+ target = self.target
131
139
  self.avg_error_map = self.property_set["vf2_avg_error_map"]
140
+ # Run rust fast path if we have no randomization
141
+ if self.seed == -1:
142
+ try:
143
+ layout = vf2_layout_pass(
144
+ dag,
145
+ target,
146
+ self.strict_direction,
147
+ self.call_limit,
148
+ self.time_limit,
149
+ self.max_trials,
150
+ self.avg_error_map,
151
+ )
152
+ except MultiQEncountered:
153
+ self.property_set["VF2Layout_stop_reason"] = VF2LayoutStopReason.MORE_THAN_2Q
154
+ return
155
+ if layout is None:
156
+ self.property_set["VF2Layout_stop_reason"] = VF2LayoutStopReason.NO_SOLUTION_FOUND
157
+ return
158
+
159
+ self.property_set["VF2Layout_stop_reason"] = VF2LayoutStopReason.SOLUTION_FOUND
160
+ mapping = {dag.qubits[virt]: phys for virt, phys in layout.items()}
161
+ chosen_layout = Layout(mapping)
162
+ self.property_set["layout"] = chosen_layout
163
+ for reg in dag.qregs.values():
164
+ self.property_set["layout"].add_register(reg)
165
+ return
166
+ # We can't use the rust fast path because we have a seed set, or no target so continue with
167
+ # the python path
132
168
  if self.avg_error_map is None:
133
- self.avg_error_map = vf2_utils.build_average_error_map(self.target, self.coupling_map)
169
+ self.avg_error_map = vf2_utils.build_average_error_map(target, coupling_map)
134
170
 
135
171
  result = vf2_utils.build_interaction_graph(dag, self.strict_direction)
136
172
  if result is None:
@@ -140,12 +176,12 @@ class VF2Layout(AnalysisPass):
140
176
  scoring_edge_list = vf2_utils.build_edge_list(im_graph)
141
177
  scoring_bit_list = vf2_utils.build_bit_list(im_graph, im_graph_node_map)
142
178
  cm_graph, cm_nodes = vf2_utils.shuffle_coupling_graph(
143
- self.coupling_map, self.seed, self.strict_direction
179
+ coupling_map, self.seed, self.strict_direction
144
180
  )
145
181
  # Filter qubits without any supported operations. If they don't support any operations
146
182
  # They're not valid for layout selection
147
- if self.target is not None and self.target.qargs is not None:
148
- has_operations = set(itertools.chain.from_iterable(self.target.qargs))
183
+ if target is not None and target.qargs is not None:
184
+ has_operations = set(itertools.chain.from_iterable(target.qargs))
149
185
  to_remove = set(range(len(cm_nodes))).difference(has_operations)
150
186
  if to_remove:
151
187
  cm_graph.remove_nodes_from([cm_nodes[i] for i in to_remove])
@@ -156,7 +192,7 @@ class VF2Layout(AnalysisPass):
156
192
  # mapping in the search space if no other limits are set
157
193
  if self.max_trials is None and self.call_limit is None and self.time_limit is None:
158
194
  im_graph_edge_count = len(im_graph.edge_list())
159
- cm_graph_edge_count = len(self.coupling_map.graph.edge_list())
195
+ cm_graph_edge_count = len(coupling_map.graph.edge_list())
160
196
  self.max_trials = max(im_graph_edge_count, cm_graph_edge_count) + 15
161
197
 
162
198
  logger.debug("Running VF2 to find mappings")
@@ -20,6 +20,7 @@ from rustworkx import PyDiGraph, PyGraph, connected_components
20
20
 
21
21
  from qiskit.circuit import ForLoopOp
22
22
  from qiskit.converters import circuit_to_dag
23
+ from qiskit.transpiler.target import Target
23
24
  from qiskit._accelerate import vf2_layout
24
25
  from qiskit._accelerate.nlayout import NLayout
25
26
  from qiskit._accelerate.error_map import ErrorMap
@@ -141,6 +142,15 @@ def score_layout(
141
142
  )
142
143
 
143
144
 
145
+ def build_dummy_target(coupling_map) -> Target:
146
+ """Build a dummy target with no error rates that represents the coupling in ``coupling_map``."""
147
+ # The choice of basis gates is completely arbitrary, and we have no source of error rates.
148
+ # We just want _something_ to represent the coupling constraints.
149
+ return Target.from_configuration(
150
+ basis_gates=["u", "cx"], num_qubits=coupling_map.size(), coupling_map=coupling_map
151
+ )
152
+
153
+
144
154
  def build_average_error_map(target, coupling_map):
145
155
  """Build an average error map used for scoring layouts pre-basis translation."""
146
156
  num_qubits = 0
@@ -39,4 +39,4 @@ from .remove_identity_equiv import RemoveIdentityEquivalent
39
39
  from .split_2q_unitaries import Split2QUnitaries
40
40
  from .collect_and_collapse import CollectAndCollapse
41
41
  from .contract_idle_wires_in_control_flow import ContractIdleWiresInControlFlow
42
- from .light_cone import LightCone
42
+ from .optimize_clifford_t import OptimizeCliffordT
@@ -19,6 +19,7 @@ from qiskit.transpiler.basepasses import TransformationPass
19
19
  from qiskit.transpiler.passes.utils import control_flow
20
20
  from qiskit.synthesis.one_qubit import one_qubit_decompose
21
21
  from qiskit._accelerate import euler_one_qubit_decomposer
22
+ from qiskit._accelerate import optimize_1q_gates_decomposition
22
23
  from qiskit.circuit.library.standard_gates import (
23
24
  UGate,
24
25
  PhaseGate,
@@ -207,7 +208,7 @@ class Optimize1qGatesDecomposition(TransformationPass):
207
208
  Returns:
208
209
  DAGCircuit: the optimized DAG.
209
210
  """
210
- euler_one_qubit_decomposer.optimize_1q_gates_decomposition(
211
+ optimize_1q_gates_decomposition.optimize_1q_gates_decomposition(
211
212
  dag,
212
213
  target=self._target,
213
214
  global_decomposers=self._global_decomposers,
@@ -0,0 +1,68 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2025.
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
+ """Combine consecutive T/Tdg gates in a Clifford+T circuit."""
14
+
15
+ from qiskit.dagcircuit import DAGCircuit
16
+ from qiskit.transpiler.basepasses import TransformationPass
17
+ from qiskit.circuit.library import SGate, SdgGate
18
+
19
+
20
+ class OptimizeCliffordT(TransformationPass):
21
+ """An optimization pass for Clifford+T circuits.
22
+
23
+ Currently all the pass does is merging pairs of consecutive T-gates into
24
+ S-gates, and pair of consecutive Tdg-gates into Sdg-gates.
25
+ """
26
+
27
+ def run(self, dag: DAGCircuit):
28
+ """
29
+ Run the OptimizeCliffordT pass on `dag`.
30
+
31
+ Args:
32
+ dag: The directed acyclic graph to run on.
33
+
34
+ Returns:
35
+ DAGCircuit: Transformed DAG.
36
+ """
37
+
38
+ new_dag = dag.copy_empty_like()
39
+
40
+ nodes = list(dag.topological_op_nodes())
41
+ num_nodes = len(nodes)
42
+ idx = 0
43
+
44
+ while idx < num_nodes - 1:
45
+ cur_node = nodes[idx]
46
+ next_node = nodes[idx + 1]
47
+ if cur_node.name == "t" and next_node.name == "t" and cur_node.qargs == next_node.qargs:
48
+ # Combine two consecutive T-gates into an S-gate
49
+ new_dag.apply_operation_back(SGate(), cur_node.qargs, cur_node.cargs)
50
+ idx += 2
51
+ elif (
52
+ nodes[idx].name == "tdg"
53
+ and nodes[idx + 1].name == "tdg"
54
+ and nodes[idx].qargs == nodes[idx + 1].qargs
55
+ ):
56
+ # Combine two consecutive Tdg-gates into an Sdg-gate
57
+ new_dag.apply_operation_back(SdgGate(), cur_node.qargs, cur_node.cargs)
58
+ idx += 2
59
+ else:
60
+ new_dag.apply_operation_back(cur_node.op, cur_node.qargs, cur_node.cargs)
61
+ idx += 1
62
+
63
+ # Handle the last element (if any)
64
+ if idx == num_nodes - 1:
65
+ cur_node = nodes[idx]
66
+ new_dag.apply_operation_back(cur_node.op, cur_node.qargs, cur_node.cargs)
67
+
68
+ return new_dag
@@ -497,14 +497,8 @@ class TemplateSubstitution:
497
497
  """
498
498
  import sympy as sym
499
499
 
500
- if _optionals.HAS_SYMENGINE:
501
- import symengine
502
-
503
- # Converts Sympy expressions to Symengine ones.
504
- to_native_symbolic = symengine.sympify
505
- else:
506
- # Our native form is sympy, so we don't need to do anything.
507
- to_native_symbolic = lambda x: x
500
+ # Our native form is sympy, so we don't need to do anything.
501
+ to_native_symbolic = lambda x: x
508
502
 
509
503
  circuit_params, template_params = [], []
510
504
  # Set of all parameter names that are present in the circuits to be optimized.
@@ -596,7 +590,7 @@ class TemplateSubstitution:
596
590
  return None
597
591
  # If there's multiple solutions, arbitrarily pick the first one.
598
592
  sol = {
599
- param.name: ParameterExpression(circ_dict, to_native_symbolic(expr))
593
+ param.name: ParameterExpression(circ_dict, str(to_native_symbolic(expr)))
600
594
  for param, expr in sym_sol[0].items()
601
595
  }
602
596
  fake_bind = {key: sol[key.name] for key in temp_symbols}
@@ -210,6 +210,8 @@ class SabreSwap(TransformationPass):
210
210
  " This circuit cannot be routed to this device."
211
211
  )
212
212
 
213
+ # In our defaults, the basic heuristic shouldn't scale by size; if it does, it's liable to
214
+ # get the algorithm stuck. See https://github.com/Qiskit/qiskit/pull/14458 for more.
213
215
  if isinstance(self.heuristic, Heuristic):
214
216
  heuristic = self.heuristic
215
217
  elif self.heuristic == "basic":
@@ -219,13 +221,13 @@ class SabreSwap(TransformationPass):
219
221
  elif self.heuristic == "lookahead":
220
222
  heuristic = (
221
223
  Heuristic(attempt_limit=10 * num_dag_qubits)
222
- .with_basic(1.0, SetScaling.Size)
224
+ .with_basic(1.0, SetScaling.Constant)
223
225
  .with_lookahead(0.5, 20, SetScaling.Size)
224
226
  )
225
227
  elif self.heuristic == "decay":
226
228
  heuristic = (
227
229
  Heuristic(attempt_limit=10 * num_dag_qubits)
228
- .with_basic(1.0, SetScaling.Size)
230
+ .with_basic(1.0, SetScaling.Constant)
229
231
  .with_lookahead(0.5, 20, SetScaling.Size)
230
232
  .with_decay(0.001, 5)
231
233
  )
@@ -15,22 +15,18 @@ import itertools
15
15
  from typing import Iterable, Union, Optional, List, Tuple
16
16
  from math import floor, log10
17
17
 
18
- from qiskit.circuit import SwitchCaseOp, Clbit, ClassicalRegister, Barrier
19
- from qiskit.circuit.controlflow import condition_resources, node_resources
18
+ from qiskit.circuit import Barrier
19
+ from qiskit.circuit.library import SwapGate
20
20
  from qiskit.dagcircuit import (
21
21
  DAGOpNode,
22
+ DAGOutNode,
23
+ DAGInNode,
22
24
  DAGDepNode,
23
25
  DAGDependency,
24
26
  DAGCircuit,
25
- DAGOutNode,
26
- DAGInNode,
27
27
  )
28
28
  from qiskit.transpiler.basepasses import TransformationPass
29
29
  from qiskit.transpiler.layout import Layout
30
- from qiskit.transpiler.passes.routing.sabre_swap import _build_sabre_dag, _apply_sabre_result
31
-
32
- from qiskit._accelerate import star_prerouting
33
- from qiskit._accelerate.nlayout import NLayout
34
30
 
35
31
 
36
32
  class StarBlock:
@@ -232,7 +228,7 @@ class StarPreRouting(TransformationPass):
232
228
  return (
233
229
  len(node.qargs) <= 2
234
230
  and len(node.cargs) == 0
235
- and getattr(node, "condition", None) is None
231
+ and getattr(node.op, "condition", None) is None
236
232
  and not isinstance(node.op, Barrier)
237
233
  )
238
234
 
@@ -318,24 +314,33 @@ class StarPreRouting(TransformationPass):
318
314
  new_dag: a dag specifying the pre-routed circuit
319
315
  qubit_mapping: the final qubit mapping after pre-routing
320
316
  """
321
- # Convert the DAG to a SabreDAG
322
- num_qubits = len(dag.qubits)
323
- canonical_register = dag.qregs["q"]
324
- current_layout = Layout.generate_trivial_layout(canonical_register)
325
- qubit_indices = {bit: idx for idx, bit in enumerate(canonical_register)}
326
- layout_mapping = {qubit_indices[k]: v for k, v in current_layout.get_virtual_bits().items()}
327
- initial_layout = NLayout(layout_mapping, num_qubits, num_qubits)
328
- sabre_dag, circuit_to_dag_dict = _build_sabre_dag(dag, num_qubits, qubit_indices)
329
-
330
- # Extract the nodes from the blocks for the Rust representation
331
- rust_blocks = [
332
- (block.center is not None, _extract_nodes(block.get_nodes(), dag)) for block in blocks
317
+ node_to_block_id = {}
318
+ for i, block in enumerate(blocks):
319
+ for node in block.get_nodes():
320
+ node_to_block_id[node] = i
321
+
322
+ new_dag = dag.copy_empty_like()
323
+ processed_block_ids = set()
324
+ qubit_mapping = list(range(len(dag.qubits)))
325
+
326
+ def _apply_mapping(qargs, qubit_mapping, qubits):
327
+ return tuple(qubits[qubit_mapping[dag.find_bit(qubit).index]] for qubit in qargs)
328
+
329
+ is_first_star = True
330
+ last_2q_gate = [
331
+ op
332
+ for op in reversed(processing_order)
333
+ if ((len(op.qargs) > 1) and (op.name != "barrier"))
333
334
  ]
335
+ if len(last_2q_gate) > 0:
336
+ last_2q_gate = last_2q_gate[0]
337
+ else:
338
+ last_2q_gate = None
334
339
 
335
- # Determine the processing order of the nodes in the DAG for the Rust representation
336
340
  int_digits = floor(log10(len(processing_order))) + 1
337
341
  processing_order_index_map = {
338
- node: f"a{index:0{int_digits}}" for index, node in enumerate(processing_order)
342
+ node: f"a{str(index).zfill(int(int_digits))}"
343
+ for index, node in enumerate(processing_order)
339
344
  }
340
345
 
341
346
  def tie_breaker_key(node):
@@ -348,61 +353,81 @@ class StarPreRouting(TransformationPass):
348
353
  f"{dag.find_bit(q).index:04d}" for q in itertools.chain(node.qargs, node.cargs)
349
354
  )
350
355
 
351
- rust_processing_order = _extract_nodes(dag.topological_op_nodes(key=tie_breaker_key), dag)
352
-
353
- # Run the star prerouting algorithm to obtain the new DAG and qubit mapping
354
- *sabre_result, qubit_mapping = star_prerouting.star_preroute(
355
- sabre_dag, rust_blocks, rust_processing_order
356
- )
357
-
358
- res_dag = _apply_sabre_result(
359
- dag.copy_empty_like(),
360
- dag,
361
- sabre_result,
362
- initial_layout,
363
- dag.qubits,
364
- circuit_to_dag_dict,
365
- )
366
-
367
- return res_dag, qubit_mapping
368
-
369
-
370
- def _extract_nodes(nodes, dag):
371
- """Extract and format node information for Rust representation used in SabreDAG.
372
-
373
- Each node is represented as a tuple containing:
374
- - Node ID (int): The unique identifier of the node in the DAG.
375
- - Qubit indices (list of int): Indices of qubits involved in the node's operation.
376
- - Classical bit indices (set of int): Indices of classical bits involved in the node's operation.
377
- - Directive flag (bool): Indicates whether the operation is a directive (True) or not (False).
378
-
379
- Args:
380
- nodes (list[DAGOpNode]): List of DAGOpNode objects to extract information from.
381
- dag (DAGCircuit): DAGCircuit object containing the circuit structure.
382
-
383
- Returns:
384
- list of tuples: Each tuple contains information about a node in the format described above.
385
- """
386
- extracted_node_info = []
387
- for node in nodes:
388
- qubit_indices = [dag.find_bit(qubit).index for qubit in node.qargs]
389
- classical_bit_indices = set()
390
-
391
- if getattr(node, "condition", None) is not None:
392
- classical_bit_indices.update(condition_resources(node.op.condition).clbits)
393
-
394
- if isinstance(node.op, SwitchCaseOp):
395
- switch_case_target = node.op.target
396
- if isinstance(switch_case_target, Clbit):
397
- classical_bit_indices.add(switch_case_target)
398
- elif isinstance(switch_case_target, ClassicalRegister):
399
- classical_bit_indices.update(switch_case_target)
400
- else: # Assume target is an expression involving classical bits
401
- classical_bit_indices.update(node_resources(switch_case_target).clbits)
402
-
403
- is_directive = getattr(node.op, "_directive", False)
404
- extracted_node_info.append(
405
- (node._node_id, qubit_indices, classical_bit_indices, is_directive)
406
- )
407
-
408
- return extracted_node_info
356
+ for node in dag.topological_op_nodes(key=tie_breaker_key):
357
+ block_id = node_to_block_id.get(node, None)
358
+ if block_id is not None:
359
+ if block_id in processed_block_ids:
360
+ continue
361
+
362
+ processed_block_ids.add(block_id)
363
+
364
+ # process the whole block
365
+ block = blocks[block_id]
366
+ sequence = block.nodes
367
+ center_node = block.center
368
+
369
+ if len(sequence) == 2:
370
+ for inner_node in sequence:
371
+ new_dag.apply_operation_back(
372
+ inner_node.op,
373
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
374
+ inner_node.cargs,
375
+ check=False,
376
+ )
377
+ continue
378
+ swap_source = None
379
+ prev = None
380
+ for inner_node in sequence:
381
+ if (len(inner_node.qargs) == 1) or (inner_node.qargs == prev):
382
+ new_dag.apply_operation_back(
383
+ inner_node.op,
384
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
385
+ inner_node.cargs,
386
+ check=False,
387
+ )
388
+ continue
389
+ if is_first_star and swap_source is None:
390
+ swap_source = center_node
391
+ new_dag.apply_operation_back(
392
+ inner_node.op,
393
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
394
+ inner_node.cargs,
395
+ check=False,
396
+ )
397
+
398
+ prev = inner_node.qargs
399
+ continue
400
+ # place 2q-gate and subsequent swap gate
401
+ new_dag.apply_operation_back(
402
+ inner_node.op,
403
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
404
+ inner_node.cargs,
405
+ check=False,
406
+ )
407
+
408
+ if not inner_node is last_2q_gate and not isinstance(inner_node.op, Barrier):
409
+ new_dag.apply_operation_back(
410
+ SwapGate(),
411
+ _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits),
412
+ inner_node.cargs,
413
+ check=False,
414
+ )
415
+ # Swap mapping
416
+ index_0 = dag.find_bit(inner_node.qargs[0]).index
417
+ index_1 = dag.find_bit(inner_node.qargs[1]).index
418
+ qubit_mapping[index_1], qubit_mapping[index_0] = (
419
+ qubit_mapping[index_0],
420
+ qubit_mapping[index_1],
421
+ )
422
+
423
+ prev = inner_node.qargs
424
+ is_first_star = False
425
+ else:
426
+ # the node is not part of a block
427
+ new_dag.apply_operation_back(
428
+ node.op,
429
+ _apply_mapping(node.qargs, qubit_mapping, dag.qubits),
430
+ node.cargs,
431
+ check=False,
432
+ )
433
+ return new_dag, qubit_mapping
@@ -14,7 +14,7 @@
14
14
 
15
15
  from .scheduling import ALAPScheduleAnalysis, ASAPScheduleAnalysis, SetIOLatency
16
16
  from .time_unit_conversion import TimeUnitConversion
17
- from .padding import PadDelay, PadDynamicalDecoupling
17
+ from .padding import PadDelay, PadDynamicalDecoupling, ContextAwareDynamicalDecoupling
18
18
  from .alignments import InstructionDurationCheck, ConstrainedReschedule
19
19
 
20
20
  # For backward compatibility
@@ -59,7 +59,7 @@ class InstructionDurationCheck(AnalysisPass):
59
59
  self.property_set["reschedule_required"] = False
60
60
 
61
61
  # Rescheduling is not necessary
62
- if (self.acquire_align == 1 and self.pulse_align == 1) or dag._num_stretches != 0:
62
+ if (self.acquire_align == 1 and self.pulse_align == 1) or dag.num_stretches != 0:
63
63
  return
64
64
 
65
65
  # Check delay durations
@@ -12,5 +12,6 @@
12
12
 
13
13
  """Scheduling pass to fill idle times with gate sequence."""
14
14
 
15
+ from .context_aware_dynamical_decoupling import ContextAwareDynamicalDecoupling
15
16
  from .dynamical_decoupling import PadDynamicalDecoupling
16
17
  from .pad_delay import PadDelay