qiskit 1.0.2__cp38-abi3-win32.whl → 1.1.0__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 (263) 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/__init__.py +5 -10
  6. qiskit/assembler/disassemble.py +5 -6
  7. qiskit/circuit/__init__.py +1061 -232
  8. qiskit/circuit/_classical_resource_map.py +10 -6
  9. qiskit/circuit/_utils.py +18 -8
  10. qiskit/circuit/annotated_operation.py +21 -0
  11. qiskit/circuit/barrier.py +10 -13
  12. qiskit/circuit/bit.py +0 -1
  13. qiskit/circuit/classical/__init__.py +2 -2
  14. qiskit/circuit/classical/expr/__init__.py +39 -5
  15. qiskit/circuit/classical/expr/constructors.py +84 -1
  16. qiskit/circuit/classical/expr/expr.py +83 -13
  17. qiskit/circuit/classical/expr/visitors.py +83 -0
  18. qiskit/circuit/classical/types/__init__.py +5 -4
  19. qiskit/circuit/classicalfunction/__init__.py +1 -0
  20. qiskit/circuit/commutation_checker.py +86 -51
  21. qiskit/circuit/controlflow/_builder_utils.py +9 -1
  22. qiskit/circuit/controlflow/break_loop.py +8 -22
  23. qiskit/circuit/controlflow/builder.py +116 -1
  24. qiskit/circuit/controlflow/continue_loop.py +8 -22
  25. qiskit/circuit/controlflow/control_flow.py +47 -8
  26. qiskit/circuit/controlflow/for_loop.py +8 -23
  27. qiskit/circuit/controlflow/if_else.py +13 -27
  28. qiskit/circuit/controlflow/switch_case.py +14 -21
  29. qiskit/circuit/controlflow/while_loop.py +9 -23
  30. qiskit/circuit/controlledgate.py +2 -2
  31. qiskit/circuit/delay.py +7 -5
  32. qiskit/circuit/gate.py +20 -7
  33. qiskit/circuit/instruction.py +31 -30
  34. qiskit/circuit/instructionset.py +9 -22
  35. qiskit/circuit/library/__init__.py +3 -13
  36. qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
  37. qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
  38. qiskit/circuit/library/blueprintcircuit.py +29 -7
  39. qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
  40. qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
  41. qiskit/circuit/library/generalized_gates/isometry.py +51 -254
  42. qiskit/circuit/library/generalized_gates/pauli.py +2 -2
  43. qiskit/circuit/library/generalized_gates/permutation.py +4 -1
  44. qiskit/circuit/library/generalized_gates/rv.py +15 -11
  45. qiskit/circuit/library/generalized_gates/uc.py +2 -98
  46. qiskit/circuit/library/generalized_gates/unitary.py +9 -4
  47. qiskit/circuit/library/hamiltonian_gate.py +11 -5
  48. qiskit/circuit/library/n_local/efficient_su2.py +5 -5
  49. qiskit/circuit/library/n_local/n_local.py +100 -49
  50. qiskit/circuit/library/n_local/two_local.py +3 -59
  51. qiskit/circuit/library/overlap.py +3 -3
  52. qiskit/circuit/library/phase_oracle.py +1 -1
  53. qiskit/circuit/library/quantum_volume.py +39 -38
  54. qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
  55. qiskit/circuit/library/standard_gates/global_phase.py +4 -2
  56. qiskit/circuit/library/standard_gates/i.py +1 -2
  57. qiskit/circuit/library/standard_gates/iswap.py +1 -2
  58. qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
  59. qiskit/circuit/library/standard_gates/p.py +31 -15
  60. qiskit/circuit/library/standard_gates/r.py +4 -3
  61. qiskit/circuit/library/standard_gates/rx.py +7 -4
  62. qiskit/circuit/library/standard_gates/rxx.py +4 -3
  63. qiskit/circuit/library/standard_gates/ry.py +7 -4
  64. qiskit/circuit/library/standard_gates/ryy.py +4 -3
  65. qiskit/circuit/library/standard_gates/rz.py +7 -4
  66. qiskit/circuit/library/standard_gates/rzx.py +4 -3
  67. qiskit/circuit/library/standard_gates/rzz.py +4 -3
  68. qiskit/circuit/library/standard_gates/s.py +4 -8
  69. qiskit/circuit/library/standard_gates/t.py +2 -4
  70. qiskit/circuit/library/standard_gates/u.py +16 -11
  71. qiskit/circuit/library/standard_gates/u1.py +6 -2
  72. qiskit/circuit/library/standard_gates/u2.py +4 -2
  73. qiskit/circuit/library/standard_gates/u3.py +9 -5
  74. qiskit/circuit/library/standard_gates/x.py +22 -11
  75. qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
  76. qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
  77. qiskit/circuit/library/standard_gates/z.py +1 -2
  78. qiskit/circuit/measure.py +4 -1
  79. qiskit/circuit/operation.py +13 -8
  80. qiskit/circuit/parameter.py +11 -6
  81. qiskit/circuit/quantumcircuit.py +1910 -260
  82. qiskit/circuit/quantumcircuitdata.py +2 -2
  83. qiskit/circuit/reset.py +5 -2
  84. qiskit/circuit/store.py +95 -0
  85. qiskit/compiler/assembler.py +22 -22
  86. qiskit/compiler/transpiler.py +63 -112
  87. qiskit/converters/__init__.py +17 -2
  88. qiskit/converters/circuit_to_dag.py +7 -0
  89. qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
  90. qiskit/converters/circuit_to_gate.py +2 -0
  91. qiskit/converters/circuit_to_instruction.py +22 -0
  92. qiskit/converters/dag_to_circuit.py +4 -0
  93. qiskit/converters/dag_to_dagdependency_v2.py +44 -0
  94. qiskit/dagcircuit/collect_blocks.py +15 -10
  95. qiskit/dagcircuit/dagcircuit.py +434 -124
  96. qiskit/dagcircuit/dagdependency.py +19 -12
  97. qiskit/dagcircuit/dagdependency_v2.py +641 -0
  98. qiskit/dagcircuit/dagdepnode.py +19 -16
  99. qiskit/dagcircuit/dagnode.py +14 -4
  100. qiskit/passmanager/passmanager.py +11 -11
  101. qiskit/primitives/__init__.py +22 -12
  102. qiskit/primitives/backend_estimator.py +3 -5
  103. qiskit/primitives/backend_estimator_v2.py +410 -0
  104. qiskit/primitives/backend_sampler_v2.py +287 -0
  105. qiskit/primitives/base/base_estimator.py +4 -9
  106. qiskit/primitives/base/base_sampler.py +2 -2
  107. qiskit/primitives/containers/__init__.py +6 -4
  108. qiskit/primitives/containers/bit_array.py +293 -2
  109. qiskit/primitives/containers/data_bin.py +123 -50
  110. qiskit/primitives/containers/estimator_pub.py +10 -3
  111. qiskit/primitives/containers/observables_array.py +2 -2
  112. qiskit/primitives/containers/pub_result.py +1 -1
  113. qiskit/primitives/containers/sampler_pub.py +19 -3
  114. qiskit/primitives/containers/sampler_pub_result.py +74 -0
  115. qiskit/primitives/containers/shape.py +4 -4
  116. qiskit/primitives/statevector_estimator.py +4 -4
  117. qiskit/primitives/statevector_sampler.py +7 -12
  118. qiskit/providers/__init__.py +65 -34
  119. qiskit/providers/backend.py +2 -2
  120. qiskit/providers/backend_compat.py +8 -10
  121. qiskit/providers/basic_provider/__init__.py +2 -23
  122. qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
  123. qiskit/providers/basic_provider/basic_simulator.py +81 -21
  124. qiskit/providers/fake_provider/__init__.py +1 -1
  125. qiskit/providers/fake_provider/fake_1q.py +1 -1
  126. qiskit/providers/fake_provider/fake_backend.py +3 -408
  127. qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
  128. qiskit/providers/models/__init__.py +2 -2
  129. qiskit/providers/provider.py +16 -0
  130. qiskit/pulse/builder.py +4 -1
  131. qiskit/pulse/parameter_manager.py +60 -4
  132. qiskit/pulse/schedule.py +29 -13
  133. qiskit/pulse/utils.py +61 -20
  134. qiskit/qasm2/__init__.py +1 -5
  135. qiskit/qasm2/parse.py +1 -4
  136. qiskit/qasm3/__init__.py +42 -5
  137. qiskit/qasm3/ast.py +19 -0
  138. qiskit/qasm3/exporter.py +178 -106
  139. qiskit/qasm3/printer.py +27 -5
  140. qiskit/qobj/converters/pulse_instruction.py +6 -6
  141. qiskit/qpy/__init__.py +299 -67
  142. qiskit/qpy/binary_io/circuits.py +216 -47
  143. qiskit/qpy/binary_io/schedules.py +42 -36
  144. qiskit/qpy/binary_io/value.py +201 -22
  145. qiskit/qpy/common.py +1 -1
  146. qiskit/qpy/exceptions.py +20 -0
  147. qiskit/qpy/formats.py +29 -0
  148. qiskit/qpy/type_keys.py +21 -0
  149. qiskit/quantum_info/analysis/distance.py +3 -3
  150. qiskit/quantum_info/analysis/make_observable.py +2 -1
  151. qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
  152. qiskit/quantum_info/operators/channel/chi.py +9 -8
  153. qiskit/quantum_info/operators/channel/choi.py +10 -9
  154. qiskit/quantum_info/operators/channel/kraus.py +2 -1
  155. qiskit/quantum_info/operators/channel/ptm.py +10 -9
  156. qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
  157. qiskit/quantum_info/operators/channel/stinespring.py +2 -1
  158. qiskit/quantum_info/operators/channel/superop.py +12 -11
  159. qiskit/quantum_info/operators/channel/transformations.py +12 -11
  160. qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
  161. qiskit/quantum_info/operators/operator.py +43 -30
  162. qiskit/quantum_info/operators/scalar_op.py +10 -9
  163. qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
  164. qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
  165. qiskit/quantum_info/operators/symplectic/pauli.py +53 -6
  166. qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
  167. qiskit/quantum_info/operators/symplectic/random.py +3 -2
  168. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +61 -36
  169. qiskit/quantum_info/states/densitymatrix.py +13 -13
  170. qiskit/quantum_info/states/stabilizerstate.py +3 -3
  171. qiskit/quantum_info/states/statevector.py +14 -13
  172. qiskit/quantum_info/states/utils.py +5 -3
  173. qiskit/result/__init__.py +6 -0
  174. qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
  175. qiskit/result/mitigation/local_readout_mitigator.py +2 -1
  176. qiskit/result/mitigation/utils.py +3 -2
  177. qiskit/scheduler/__init__.py +10 -1
  178. qiskit/scheduler/methods/__init__.py +1 -8
  179. qiskit/synthesis/__init__.py +3 -6
  180. qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
  181. qiskit/synthesis/evolution/lie_trotter.py +7 -14
  182. qiskit/synthesis/evolution/qdrift.py +3 -4
  183. qiskit/synthesis/linear/cnot_synth.py +1 -3
  184. qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
  185. qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
  186. qiskit/synthesis/permutation/__init__.py +1 -0
  187. qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
  188. qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
  189. qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
  190. qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
  191. qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
  192. qiskit/synthesis/unitary/aqc/__init__.py +1 -1
  193. qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
  194. qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
  195. qiskit/synthesis/unitary/qsd.py +3 -2
  196. qiskit/transpiler/__init__.py +7 -3
  197. qiskit/transpiler/layout.py +140 -61
  198. qiskit/transpiler/passes/__init__.py +10 -2
  199. qiskit/transpiler/passes/basis/basis_translator.py +9 -4
  200. qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
  201. qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
  202. qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
  203. qiskit/transpiler/passes/layout/apply_layout.py +8 -3
  204. qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
  205. qiskit/transpiler/passes/layout/set_layout.py +1 -1
  206. qiskit/transpiler/passes/optimization/__init__.py +2 -0
  207. qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
  208. qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
  209. qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
  210. qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
  211. qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
  212. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
  213. qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
  214. qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
  215. qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
  216. qiskit/transpiler/passes/routing/__init__.py +1 -0
  217. qiskit/transpiler/passes/routing/basic_swap.py +13 -2
  218. qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +8 -1
  219. qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
  220. qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
  221. qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
  222. qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
  223. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  224. qiskit/transpiler/passes/scheduling/alap.py +1 -2
  225. qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
  226. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
  227. qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
  228. qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
  229. qiskit/transpiler/passes/scheduling/asap.py +1 -2
  230. qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
  231. qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
  232. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
  233. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
  234. qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
  235. qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
  236. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
  237. qiskit/transpiler/passes/utils/gates_basis.py +3 -3
  238. qiskit/transpiler/passmanager.py +44 -1
  239. qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
  240. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
  241. qiskit/transpiler/preset_passmanagers/common.py +4 -6
  242. qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
  243. qiskit/utils/__init__.py +3 -2
  244. qiskit/utils/optionals.py +6 -2
  245. qiskit/utils/parallel.py +24 -15
  246. qiskit/visualization/array.py +1 -1
  247. qiskit/visualization/bloch.py +2 -3
  248. qiskit/visualization/circuit/matplotlib.py +44 -14
  249. qiskit/visualization/circuit/text.py +38 -18
  250. qiskit/visualization/counts_visualization.py +3 -6
  251. qiskit/visualization/dag_visualization.py +6 -7
  252. qiskit/visualization/gate_map.py +9 -1
  253. qiskit/visualization/pulse_v2/interface.py +8 -3
  254. qiskit/visualization/state_visualization.py +3 -2
  255. qiskit/visualization/timeline/interface.py +18 -8
  256. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/METADATA +12 -8
  257. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/RECORD +261 -251
  258. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/WHEEL +1 -1
  259. qiskit/_qasm2.pyd +0 -0
  260. qiskit/_qasm3.pyd +0 -0
  261. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/LICENSE.txt +0 -0
  262. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/entry_points.txt +0 -0
  263. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/top_level.txt +0 -0
@@ -24,7 +24,6 @@ Unitary Synthesis Plugin (in :mod:`qiskit.transpiler.passes.synthesis.unitary_sy
24
24
  from __future__ import annotations
25
25
  from math import pi, inf, isclose
26
26
  from typing import Any
27
- from copy import deepcopy
28
27
  from itertools import product
29
28
  from functools import partial
30
29
  import numpy as np
@@ -35,10 +34,12 @@ from qiskit.transpiler.basepasses import TransformationPass
35
34
  from qiskit.transpiler.exceptions import TranspilerError
36
35
  from qiskit.dagcircuit.dagcircuit import DAGCircuit
37
36
  from qiskit.synthesis.one_qubit import one_qubit_decompose
37
+ from qiskit.transpiler.passes.optimization.optimize_1q_decomposition import _possible_decomposers
38
38
  from qiskit.synthesis.two_qubit.xx_decompose import XXDecomposer, XXEmbodiments
39
39
  from qiskit.synthesis.two_qubit.two_qubit_decompose import (
40
40
  TwoQubitBasisDecomposer,
41
41
  TwoQubitWeylDecomposition,
42
+ GATE_NAME_MAP,
42
43
  )
43
44
  from qiskit.quantum_info import Operator
44
45
  from qiskit.circuit import ControlFlowOp, Gate, Parameter
@@ -84,23 +85,16 @@ def _choose_kak_gate(basis_gates):
84
85
  def _choose_euler_basis(basis_gates):
85
86
  """Choose the first available 1q basis to use in the Euler decomposition."""
86
87
  basis_set = set(basis_gates or [])
87
-
88
- for basis, gates in one_qubit_decompose.ONE_QUBIT_EULER_BASIS_GATES.items():
89
-
90
- if set(gates).issubset(basis_set):
91
- return basis
92
-
88
+ decomposers = _possible_decomposers(basis_set)
89
+ if decomposers:
90
+ return decomposers[0]
93
91
  return "U"
94
92
 
95
93
 
96
94
  def _find_matching_euler_bases(target, qubit):
97
95
  """Find matching available 1q basis to use in the Euler decomposition."""
98
- euler_basis_gates = []
99
96
  basis_set = target.operation_names_for_qargs((qubit,))
100
- for basis, gates in one_qubit_decompose.ONE_QUBIT_EULER_BASIS_GATES.items():
101
- if set(gates).issubset(basis_set):
102
- euler_basis_gates.append(basis)
103
- return euler_basis_gates
97
+ return _possible_decomposers(basis_set)
104
98
 
105
99
 
106
100
  def _choose_bases(basis_gates, basis_dict=None):
@@ -153,20 +147,26 @@ def _error(circuit, target=None, qubits=None):
153
147
  of circuit as a weak proxy for error.
154
148
  """
155
149
  if target is None:
156
- return len(circuit)
150
+ if isinstance(circuit, DAGCircuit):
151
+ return len(circuit.op_nodes())
152
+ else:
153
+ return len(circuit)
157
154
  gate_fidelities = []
158
155
  gate_durations = []
159
- for inst in circuit:
160
- inst_qubits = tuple(qubits[circuit.find_bit(q).index] for q in inst.qubits)
156
+
157
+ def score_instruction(inst, inst_qubits):
161
158
  try:
162
159
  keys = target.operation_names_for_qargs(inst_qubits)
163
160
  for key in keys:
164
161
  target_op = target.operation_from_name(key)
165
- if isinstance(target_op, inst.operation.base_class) and (
162
+ if isinstance(circuit, DAGCircuit):
163
+ op = inst.op
164
+ else:
165
+ op = inst.operation
166
+ if isinstance(target_op, op.base_class) and (
166
167
  target_op.is_parameterized()
167
168
  or all(
168
- isclose(float(p1), float(p2))
169
- for p1, p2 in zip(target_op.params, inst.operation.params)
169
+ isclose(float(p1), float(p2)) for p1, p2 in zip(target_op.params, op.params)
170
170
  )
171
171
  ):
172
172
  inst_props = target[key].get(inst_qubits, None)
@@ -183,10 +183,22 @@ def _error(circuit, target=None, qubits=None):
183
183
  else:
184
184
  raise KeyError
185
185
  except KeyError as error:
186
+ if isinstance(circuit, DAGCircuit):
187
+ op = inst.op
188
+ else:
189
+ op = inst.operation
186
190
  raise TranspilerError(
187
- f"Encountered a bad synthesis. "
188
- f"Target has no {inst.operation} on qubits {qubits}."
191
+ f"Encountered a bad synthesis. " f"Target has no {op} on qubits {qubits}."
189
192
  ) from error
193
+
194
+ if isinstance(circuit, DAGCircuit):
195
+ for inst in circuit.topological_op_nodes():
196
+ inst_qubits = tuple(qubits[circuit.find_bit(q).index] for q in inst.qargs)
197
+ score_instruction(inst, inst_qubits)
198
+ else:
199
+ for inst in circuit:
200
+ inst_qubits = tuple(qubits[circuit.find_bit(q).index] for q in inst.qubits)
201
+ score_instruction(inst, inst_qubits)
190
202
  # TODO:return np.sum(gate_durations)
191
203
  return 1 - np.prod(gate_fidelities)
192
204
 
@@ -282,7 +294,7 @@ class UnitarySynthesis(TransformationPass):
282
294
  natural_direction: bool | None = None,
283
295
  synth_gates: list[str] | None = None,
284
296
  method: str = "default",
285
- min_qubits: int = None,
297
+ min_qubits: int = 0,
286
298
  plugin_config: dict = None,
287
299
  target: Target = None,
288
300
  ):
@@ -488,27 +500,55 @@ class UnitarySynthesis(TransformationPass):
488
500
  ]
489
501
  )
490
502
 
491
- for node in dag.named_nodes(*self._synth_gates):
492
- if self._min_qubits is not None and len(node.qargs) < self._min_qubits:
493
- continue
494
- synth_dag = None
495
- unitary = node.op.to_matrix()
496
- n_qubits = len(node.qargs)
497
- if (plugin_method.max_qubits is not None and n_qubits > plugin_method.max_qubits) or (
498
- plugin_method.min_qubits is not None and n_qubits < plugin_method.min_qubits
499
- ):
500
- method, kwargs = default_method, default_kwargs
503
+ out_dag = dag.copy_empty_like()
504
+ for node in dag.topological_op_nodes():
505
+ if node.op.name == "unitary" and len(node.qargs) >= self._min_qubits:
506
+ synth_dag = None
507
+ unitary = node.op.to_matrix()
508
+ n_qubits = len(node.qargs)
509
+ if (
510
+ plugin_method.max_qubits is not None and n_qubits > plugin_method.max_qubits
511
+ ) or (plugin_method.min_qubits is not None and n_qubits < plugin_method.min_qubits):
512
+ method, kwargs = default_method, default_kwargs
513
+ else:
514
+ method, kwargs = plugin_method, plugin_kwargs
515
+ if method.supports_coupling_map:
516
+ kwargs["coupling_map"] = (
517
+ self._coupling_map,
518
+ [qubit_indices[x] for x in node.qargs],
519
+ )
520
+ synth_dag = method.run(unitary, **kwargs)
521
+ if synth_dag is None:
522
+ out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
523
+ continue
524
+ if isinstance(synth_dag, DAGCircuit):
525
+ qubit_map = dict(zip(synth_dag.qubits, node.qargs))
526
+ for node in synth_dag.topological_op_nodes():
527
+ out_dag.apply_operation_back(
528
+ node.op, (qubit_map[x] for x in node.qargs), check=False
529
+ )
530
+ out_dag.global_phase += synth_dag.global_phase
531
+ else:
532
+ node_list, global_phase, gate = synth_dag
533
+ qubits = node.qargs
534
+ for (
535
+ op_name,
536
+ params,
537
+ qargs,
538
+ ) in node_list:
539
+ if op_name == "USER_GATE":
540
+ op = gate
541
+ else:
542
+ op = GATE_NAME_MAP[op_name](*params)
543
+ out_dag.apply_operation_back(
544
+ op,
545
+ (qubits[x] for x in qargs),
546
+ check=False,
547
+ )
548
+ out_dag.global_phase += global_phase
501
549
  else:
502
- method, kwargs = plugin_method, plugin_kwargs
503
- if method.supports_coupling_map:
504
- kwargs["coupling_map"] = (
505
- self._coupling_map,
506
- [qubit_indices[x] for x in node.qargs],
507
- )
508
- synth_dag = method.run(unitary, **kwargs)
509
- if synth_dag is not None:
510
- dag.substitute_node_with_dag(node, synth_dag)
511
- return dag
550
+ out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
551
+ return out_dag
512
552
 
513
553
 
514
554
  def _build_gate_lengths(props=None, target=None):
@@ -777,6 +817,13 @@ class DefaultUnitarySynthesis(plugin.UnitarySynthesisPlugin):
777
817
  )
778
818
  decomposers.append(decomposer)
779
819
 
820
+ # If our 2q basis gates are a subset of cx, ecr, or cz then we know TwoQubitBasisDecomposer
821
+ # is an ideal decomposition and there is no need to bother calculating the XX embodiments
822
+ # or try the XX decomposer
823
+ if {"cx", "cz", "ecr"}.issuperset(available_2q_basis):
824
+ self._decomposer_cache[qubits_tuple] = decomposers
825
+ return decomposers
826
+
780
827
  # possible controlled decomposers (i.e. XXDecomposer)
781
828
  controlled_basis = {k: v for k, v in available_2q_basis.items() if is_controlled(v)}
782
829
  basis_2q_fidelity = {}
@@ -808,10 +855,23 @@ class DefaultUnitarySynthesis(plugin.UnitarySynthesisPlugin):
808
855
  if basis_2q_fidelity:
809
856
  for basis_1q in available_1q_basis:
810
857
  if isinstance(pi2_basis, CXGate) and basis_1q == "ZSX":
858
+ # If we're going to use the pulse optimal decomposition
859
+ # in TwoQubitBasisDecomposer we need to compute the basis
860
+ # fidelity to use for the decomposition. Either use the
861
+ # cx error rate if approximation degree is None, or
862
+ # the approximation degree value if it's a float
863
+ if approximation_degree is None:
864
+ props = target["cx"].get(qubits_tuple)
865
+ if props is not None:
866
+ fidelity = 1.0 - getattr(props, "error", 0.0)
867
+ else:
868
+ fidelity = 1.0
869
+ else:
870
+ fidelity = approximation_degree
811
871
  pi2_decomposer = TwoQubitBasisDecomposer(
812
872
  pi2_basis,
813
873
  euler_basis=basis_1q,
814
- basis_fidelity=basis_2q_fidelity,
874
+ basis_fidelity=fidelity,
815
875
  pulse_optimize=True,
816
876
  )
817
877
  embodiments.update({pi / 2: XXEmbodiments[pi2_decomposer.gate.base_class]})
@@ -862,6 +922,20 @@ class DefaultUnitarySynthesis(plugin.UnitarySynthesisPlugin):
862
922
  decomposers2q = [decomposer2q] if decomposer2q is not None else []
863
923
  # choose the cheapest output among synthesized circuits
864
924
  synth_circuits = []
925
+ # If we have a single TwoQubitBasisDecomposer skip dag creation as we don't need to
926
+ # store and can instead manually create the synthesized gates directly in the output dag
927
+ if len(decomposers2q) == 1 and isinstance(decomposers2q[0], TwoQubitBasisDecomposer):
928
+ preferred_direction = _preferred_direction(
929
+ decomposers2q[0],
930
+ qubits,
931
+ natural_direction,
932
+ coupling_map,
933
+ gate_lengths,
934
+ gate_errors,
935
+ )
936
+ return self._synth_su4_no_dag(
937
+ unitary, decomposers2q[0], preferred_direction, approximation_degree
938
+ )
865
939
  for decomposer2q in decomposers2q:
866
940
  preferred_direction = _preferred_direction(
867
941
  decomposer2q, qubits, natural_direction, coupling_map, gate_lengths, gate_errors
@@ -882,24 +956,57 @@ class DefaultUnitarySynthesis(plugin.UnitarySynthesisPlugin):
882
956
 
883
957
  # only decompose if needed. TODO: handle basis better
884
958
  synth_circuit = qs_decomposition(unitary) if (basis_gates or target) else None
959
+ if synth_circuit is None:
960
+ return None
961
+ if isinstance(synth_circuit, DAGCircuit):
962
+ return synth_circuit
963
+ return circuit_to_dag(synth_circuit)
885
964
 
886
- synth_dag = circuit_to_dag(synth_circuit) if synth_circuit is not None else None
887
- return synth_dag
888
-
889
- def _synth_su4(self, su4_mat, decomposer2q, preferred_direction, approximation_degree):
965
+ def _synth_su4_no_dag(self, unitary, decomposer2q, preferred_direction, approximation_degree):
890
966
  approximate = not approximation_degree == 1.0
891
- synth_circ = decomposer2q(su4_mat, approximate=approximate)
967
+ synth_circ = decomposer2q._inner_decomposer(unitary, approximate=approximate)
968
+ if not preferred_direction:
969
+ return (synth_circ, synth_circ.global_phase, decomposer2q.gate)
892
970
 
971
+ synth_direction = None
893
972
  # if the gates in synthesis are in the opposite direction of the preferred direction
894
973
  # resynthesize a new operator which is the original conjugated by swaps.
895
974
  # this new operator is doubly mirrored from the original and is locally equivalent.
975
+ for op_name, _params, qubits in synth_circ:
976
+ if op_name in {"USER_GATE", "cx"}:
977
+ synth_direction = qubits
978
+ if synth_direction is not None and synth_direction != preferred_direction:
979
+ # TODO: Avoid using a dag to correct the synthesis direction
980
+ return self._reversed_synth_su4(unitary, decomposer2q, approximation_degree)
981
+ return (synth_circ, synth_circ.global_phase, decomposer2q.gate)
982
+
983
+ def _synth_su4(self, su4_mat, decomposer2q, preferred_direction, approximation_degree):
984
+ approximate = not approximation_degree == 1.0
985
+ synth_circ = decomposer2q(su4_mat, approximate=approximate, use_dag=True)
986
+ if not preferred_direction:
987
+ return synth_circ
896
988
  synth_direction = None
897
- for inst in synth_circ:
898
- if inst.operation.num_qubits == 2:
899
- synth_direction = [synth_circ.find_bit(q).index for q in inst.qubits]
900
- if preferred_direction and synth_direction != preferred_direction:
901
- su4_mat_mm = deepcopy(su4_mat)
902
- su4_mat_mm[[1, 2]] = su4_mat_mm[[2, 1]]
903
- su4_mat_mm[:, [1, 2]] = su4_mat_mm[:, [2, 1]]
904
- synth_circ = decomposer2q(su4_mat_mm, approximate=approximate).reverse_bits()
989
+ # if the gates in synthesis are in the opposite direction of the preferred direction
990
+ # resynthesize a new operator which is the original conjugated by swaps.
991
+ # this new operator is doubly mirrored from the original and is locally equivalent.
992
+ for inst in synth_circ.topological_op_nodes():
993
+ if inst.op.num_qubits == 2:
994
+ synth_direction = [synth_circ.find_bit(q).index for q in inst.qargs]
995
+ if synth_direction is not None and synth_direction != preferred_direction:
996
+ return self._reversed_synth_su4(su4_mat, decomposer2q, approximation_degree)
905
997
  return synth_circ
998
+
999
+ def _reversed_synth_su4(self, su4_mat, decomposer2q, approximation_degree):
1000
+ approximate = not approximation_degree == 1.0
1001
+ su4_mat_mm = su4_mat.copy()
1002
+ su4_mat_mm[[1, 2]] = su4_mat_mm[[2, 1]]
1003
+ su4_mat_mm[:, [1, 2]] = su4_mat_mm[:, [2, 1]]
1004
+ synth_circ = decomposer2q(su4_mat_mm, approximate=approximate, use_dag=True)
1005
+ out_dag = DAGCircuit()
1006
+ out_dag.global_phase = synth_circ.global_phase
1007
+ out_dag.add_qubits(list(reversed(synth_circ.qubits)))
1008
+ flip_bits = out_dag.qubits[::-1]
1009
+ for node in synth_circ.topological_op_nodes():
1010
+ qubits = tuple(flip_bits[synth_circ.find_bit(x).index] for x in node.qargs)
1011
+ out_dag.apply_operation_back(node.op, qubits, check=False)
1012
+ return out_dag
@@ -32,7 +32,7 @@ class GatesInBasis(AnalysisPass):
32
32
  self._basis_gates = None
33
33
  if basis_gates is not None:
34
34
  self._basis_gates = set(basis_gates).union(
35
- {"measure", "reset", "barrier", "snapshot", "delay"}
35
+ {"measure", "reset", "barrier", "snapshot", "delay", "store"}
36
36
  )
37
37
  self._target = target
38
38
 
@@ -46,8 +46,8 @@ class GatesInBasis(AnalysisPass):
46
46
 
47
47
  def _visit_target(dag, wire_map):
48
48
  for gate in dag.op_nodes():
49
- # Barrier is universal and supported by all backends
50
- if gate.name == "barrier":
49
+ # Barrier and store are assumed universal and supported by all backends
50
+ if gate.name in ("barrier", "store"):
51
51
  continue
52
52
  if not self._target.instruction_supported(
53
53
  gate.name, tuple(wire_map[bit] for bit in gate.qargs)
@@ -29,7 +29,7 @@ from qiskit.passmanager.flow_controllers import FlowControllerLinear
29
29
  from qiskit.passmanager.exceptions import PassManagerError
30
30
  from .basepasses import BasePass
31
31
  from .exceptions import TranspilerError
32
- from .layout import TranspileLayout
32
+ from .layout import TranspileLayout, Layout
33
33
 
34
34
  _CircuitsT = Union[List[QuantumCircuit], QuantumCircuit]
35
35
 
@@ -69,6 +69,7 @@ class PassManager(BasePassManager):
69
69
  ) -> QuantumCircuit:
70
70
  out_program = dag_to_circuit(passmanager_ir, copy_operations=False)
71
71
 
72
+ self._finalize_layouts(passmanager_ir)
72
73
  out_name = kwargs.get("output_name", None)
73
74
  if out_name is not None:
74
75
  out_program.name = out_name
@@ -96,6 +97,48 @@ class PassManager(BasePassManager):
96
97
 
97
98
  return out_program
98
99
 
100
+ def _finalize_layouts(self, dag):
101
+ if (virtual_permutation_layout := self.property_set["virtual_permutation_layout"]) is None:
102
+ return
103
+
104
+ self.property_set.pop("virtual_permutation_layout")
105
+
106
+ # virtual_permutation_layout is usually created before extending the layout with ancillas,
107
+ # so we extend the permutation to be identity on ancilla qubits
108
+ original_qubit_indices = self.property_set.get("original_qubit_indices", None)
109
+ for oq in original_qubit_indices:
110
+ if oq not in virtual_permutation_layout:
111
+ virtual_permutation_layout[oq] = original_qubit_indices[oq]
112
+
113
+ t_qubits = dag.qubits
114
+
115
+ if (t_initial_layout := self.property_set.get("layout", None)) is None:
116
+ t_initial_layout = Layout(dict(enumerate(t_qubits)))
117
+
118
+ if (t_final_layout := self.property_set.get("final_layout", None)) is None:
119
+ t_final_layout = Layout(dict(enumerate(t_qubits)))
120
+
121
+ # Ordered list of original qubits
122
+ original_qubits_reverse = {v: k for k, v in original_qubit_indices.items()}
123
+ original_qubits = []
124
+ for i in range(len(original_qubits_reverse)):
125
+ original_qubits.append(original_qubits_reverse[i])
126
+
127
+ virtual_permutation_layout_inv = virtual_permutation_layout.inverse(
128
+ original_qubits, original_qubits
129
+ )
130
+
131
+ t_initial_layout_inv = t_initial_layout.inverse(original_qubits, t_qubits)
132
+
133
+ # ToDo: this can possibly be made simpler
134
+ new_final_layout = t_initial_layout_inv
135
+ new_final_layout = new_final_layout.compose(virtual_permutation_layout_inv, original_qubits)
136
+ new_final_layout = new_final_layout.compose(t_initial_layout, original_qubits)
137
+ new_final_layout = new_final_layout.compose(t_final_layout, t_qubits)
138
+
139
+ self.property_set["layout"] = t_initial_layout
140
+ self.property_set["final_layout"] = new_final_layout
141
+
99
142
  def append(
100
143
  self,
101
144
  passes: Task | list[Task],
@@ -115,7 +115,7 @@ def generate_preset_pass_manager(
115
115
 
116
116
  backend (Backend): An optional backend object which can be used as the
117
117
  source of the default values for the ``basis_gates``, ``inst_map``,
118
- ``couplig_map``, ``backend_properties``, ``instruction_durations``,
118
+ ``coupling_map``, ``backend_properties``, ``instruction_durations``,
119
119
  ``timing_constraints``, and ``target``. If any of those other arguments
120
120
  are specified in addition to ``backend`` they will take precedence
121
121
  over the value contained in the backend.
@@ -136,11 +136,11 @@ def generate_preset_pass_manager(
136
136
  instruction_durations (InstructionDurations): Dictionary of duration
137
137
  (in dt) for each instruction.
138
138
  timing_constraints (TimingConstraints): Hardware time alignment restrictions.
139
- initial_layout (Layout): Initial position of virtual qubits on
139
+ initial_layout (Layout | List[int]): Initial position of virtual qubits on
140
140
  physical qubits.
141
141
  layout_method (str): The :class:`~.Pass` to use for choosing initial qubit
142
142
  placement. Valid choices are ``'trivial'``, ``'dense'``,
143
- and ``'sabre'``, representing :class:`~.TrivialLayout`, :class:`~DenseLayout` and
143
+ and ``'sabre'``, representing :class:`~.TrivialLayout`, :class:`~.DenseLayout` and
144
144
  :class:`~.SabreLayout` respectively. This can also
145
145
  be the external plugin name to use for the ``layout`` stage of the output
146
146
  :class:`~.StagedPassManager`. You can see a list of installed plugins by using
@@ -26,7 +26,7 @@ from qiskit.transpiler.passes import DenseLayout
26
26
  from qiskit.transpiler.passes import TrivialLayout
27
27
  from qiskit.transpiler.passes import CheckMap
28
28
  from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
29
- from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure
29
+ from qiskit.transpiler.passes import ElidePermutations
30
30
  from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure
31
31
  from qiskit.transpiler.preset_passmanagers import common
32
32
  from qiskit.transpiler.preset_passmanagers.plugin import (
@@ -87,7 +87,7 @@ class DefaultInitPassManager(PassManagerStagePlugin):
87
87
  pass_manager_config.unitary_synthesis_plugin_config,
88
88
  pass_manager_config.hls_config,
89
89
  )
90
- elif optimization_level in {1, 2}:
90
+ elif optimization_level == 1:
91
91
  init = PassManager()
92
92
  if (
93
93
  pass_manager_config.initial_layout
@@ -124,7 +124,7 @@ class DefaultInitPassManager(PassManagerStagePlugin):
124
124
  )
125
125
  )
126
126
 
127
- elif optimization_level == 3:
127
+ elif optimization_level in {2, 3}:
128
128
  init = common.generate_unroll_3q(
129
129
  pass_manager_config.target,
130
130
  pass_manager_config.basis_gates,
@@ -133,7 +133,7 @@ class DefaultInitPassManager(PassManagerStagePlugin):
133
133
  pass_manager_config.unitary_synthesis_plugin_config,
134
134
  pass_manager_config.hls_config,
135
135
  )
136
- init.append(OptimizeSwapBeforeMeasure())
136
+ init.append(ElidePermutations())
137
137
  init.append(RemoveDiagonalGatesBeforeMeasure())
138
138
  init.append(
139
139
  InverseCancellation(
@@ -153,9 +153,9 @@ class DefaultInitPassManager(PassManagerStagePlugin):
153
153
  ]
154
154
  )
155
155
  )
156
-
156
+ init.append(CommutativeCancellation())
157
157
  else:
158
- return TranspilerError(f"Invalid optimization level {optimization_level}")
158
+ raise TranspilerError(f"Invalid optimization level {optimization_level}")
159
159
  return init
160
160
 
161
161
 
@@ -540,16 +540,13 @@ class OptimizationPassManager(PassManagerStagePlugin):
540
540
  ]
541
541
  ),
542
542
  ]
543
+
543
544
  elif optimization_level == 2:
544
- # Steps for optimization level 2
545
545
  _opt = [
546
546
  Optimize1qGatesDecomposition(
547
547
  basis=pass_manager_config.basis_gates, target=pass_manager_config.target
548
548
  ),
549
- CommutativeCancellation(
550
- basis_gates=pass_manager_config.basis_gates,
551
- target=pass_manager_config.target,
552
- ),
549
+ CommutativeCancellation(target=pass_manager_config.target),
553
550
  ]
554
551
  elif optimization_level == 3:
555
552
  # Steps for optimization level 3
@@ -595,6 +592,27 @@ class OptimizationPassManager(PassManagerStagePlugin):
595
592
 
596
593
  if optimization_level == 3:
597
594
  optimization.append(_minimum_point_check)
595
+ elif optimization_level == 2:
596
+ optimization.append(
597
+ [
598
+ Collect2qBlocks(),
599
+ ConsolidateBlocks(
600
+ basis_gates=pass_manager_config.basis_gates,
601
+ target=pass_manager_config.target,
602
+ approximation_degree=pass_manager_config.approximation_degree,
603
+ ),
604
+ UnitarySynthesis(
605
+ pass_manager_config.basis_gates,
606
+ approximation_degree=pass_manager_config.approximation_degree,
607
+ coupling_map=pass_manager_config.coupling_map,
608
+ backend_props=pass_manager_config.backend_properties,
609
+ method=pass_manager_config.unitary_synthesis_method,
610
+ plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
611
+ target=pass_manager_config.target,
612
+ ),
613
+ ]
614
+ )
615
+ optimization.append(_depth_check + _size_check)
598
616
  else:
599
617
  optimization.append(_depth_check + _size_check)
600
618
  opt_loop = (
@@ -746,7 +764,7 @@ class DefaultLayoutPassManager(PassManagerStagePlugin):
746
764
  call_limit=int(5e6), # Set call limit to ~10s with rustworkx 0.10.2
747
765
  properties=pass_manager_config.backend_properties,
748
766
  target=pass_manager_config.target,
749
- max_trials=25000, # Limits layout scoring to < 10s on ~400 qubit devices
767
+ max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices
750
768
  )
751
769
  layout.append(
752
770
  ConditionalController(choose_layout_0, condition=_choose_layout_condition)
@@ -755,8 +773,8 @@ class DefaultLayoutPassManager(PassManagerStagePlugin):
755
773
  coupling_map,
756
774
  max_iterations=2,
757
775
  seed=pass_manager_config.seed_transpiler,
758
- swap_trials=10,
759
- layout_trials=10,
776
+ swap_trials=20,
777
+ layout_trials=20,
760
778
  skip_routing=pass_manager_config.routing_method is not None
761
779
  and pass_manager_config.routing_method != "sabre",
762
780
  )
@@ -908,8 +926,8 @@ class SabreLayoutPassManager(PassManagerStagePlugin):
908
926
  coupling_map,
909
927
  max_iterations=2,
910
928
  seed=pass_manager_config.seed_transpiler,
911
- swap_trials=10,
912
- layout_trials=10,
929
+ swap_trials=20,
930
+ layout_trials=20,
913
931
  skip_routing=pass_manager_config.routing_method is not None
914
932
  and pass_manager_config.routing_method != "sabre",
915
933
  )
@@ -584,6 +584,7 @@ def generate_scheduling(
584
584
  InstructionDurationCheck(
585
585
  acquire_alignment=timing_constraints.acquire_alignment,
586
586
  pulse_alignment=timing_constraints.pulse_alignment,
587
+ target=target,
587
588
  )
588
589
  )
589
590
  scheduling.append(
@@ -591,6 +592,7 @@ def generate_scheduling(
591
592
  ConstrainedReschedule(
592
593
  acquire_alignment=timing_constraints.acquire_alignment,
593
594
  pulse_alignment=timing_constraints.pulse_alignment,
595
+ target=target,
594
596
  ),
595
597
  condition=_require_alignment,
596
598
  )
@@ -599,6 +601,7 @@ def generate_scheduling(
599
601
  ValidatePulseGates(
600
602
  granularity=timing_constraints.granularity,
601
603
  min_length=timing_constraints.min_length,
604
+ target=target,
602
605
  )
603
606
  )
604
607
  if scheduling_method:
@@ -624,16 +627,11 @@ def get_vf2_limits(
624
627
  """
625
628
  limits = VF2Limits(None, None)
626
629
  if layout_method is None and initial_layout is None:
627
- if optimization_level == 1:
630
+ if optimization_level in {1, 2}:
628
631
  limits = VF2Limits(
629
632
  int(5e4), # Set call limit to ~100ms with rustworkx 0.10.2
630
633
  2500, # Limits layout scoring to < 600ms on ~400 qubit devices
631
634
  )
632
- elif optimization_level == 2:
633
- limits = VF2Limits(
634
- int(5e6), # Set call limit to ~10 sec with rustworkx 0.10.2
635
- 25000, # Limits layout scoring to < 6 sec on ~400 qubit devices
636
- )
637
635
  elif optimization_level == 3:
638
636
  limits = VF2Limits(
639
637
  int(3e7), # Set call limit to ~60 sec with rustworkx 0.10.2
@@ -68,7 +68,15 @@ load external plugins via corresponding entry points.
68
68
  connectivity constraints of the target backend. This does not necessarily
69
69
  need to match the directionality of the edges in the target as a later
70
70
  stage typically will adjust directional gates to match that constraint
71
- (but there is no penalty for doing that in the ``routing`` stage).
71
+ (but there is no penalty for doing that in the ``routing`` stage). The output
72
+ of this stage is also expected to have the ``final_layout`` property set field
73
+ set with a :class:`~.Layout` object that maps the :class:`.Qubit` to the
74
+ output final position of that qubit in the circuit. If there is an
75
+ existing ``final_layout`` entry in the property set (such as might be set
76
+ by an optimization pass that introduces a permutation) it is expected
77
+ that the final layout will be the composition of the two layouts (this
78
+ can be computed using :meth:`.DAGCircuit.compose`, for example:
79
+ ``second_final_layout.compose(first_final_layout, dag.qubits)``).
72
80
  * - ``translation``
73
81
  - ``qiskit.transpiler.translation``
74
82
  - ``translator``, ``synthesis``, ``unroller``
qiskit/utils/__init__.py CHANGED
@@ -44,7 +44,7 @@ Multiprocessing
44
44
  .. autofunction:: local_hardware_info
45
45
  .. autofunction:: is_main_process
46
46
 
47
- A helper function for calling a custom function with python
47
+ A helper function for calling a custom function with Python
48
48
  :class:`~concurrent.futures.ProcessPoolExecutor`. Tasks can be executed in parallel using this function.
49
49
 
50
50
  .. autofunction:: parallel_map
@@ -70,7 +70,7 @@ from .lazy_tester import LazyDependencyManager, LazyImportTester, LazySubprocess
70
70
 
71
71
  from . import optionals
72
72
 
73
- from .parallel import parallel_map
73
+ from .parallel import parallel_map, should_run_in_parallel
74
74
 
75
75
  __all__ = [
76
76
  "LazyDependencyManager",
@@ -85,4 +85,5 @@ __all__ = [
85
85
  "is_main_process",
86
86
  "apply_prefix",
87
87
  "parallel_map",
88
+ "should_run_in_parallel",
88
89
  ]
qiskit/utils/optionals.py CHANGED
@@ -307,8 +307,12 @@ HAS_Z3 = _LazyImportTester("z3", install="pip install z3-solver")
307
307
 
308
308
  HAS_GRAPHVIZ = _LazySubprocessTester(
309
309
  ("dot", "-V"),
310
- name="graphviz",
311
- install="'brew install graphviz' if on Mac, or by downloding it from their website",
310
+ name="Graphviz",
311
+ msg=(
312
+ "To install, follow the instructions at https://graphviz.org/download/."
313
+ " Qiskit needs the Graphviz binaries, which the 'graphviz' package on pip does not install."
314
+ " You must install the actual Graphviz software"
315
+ ),
312
316
  )
313
317
  HAS_PDFLATEX = _LazySubprocessTester(
314
318
  ("pdflatex", "-version"),