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
@@ -55,6 +55,9 @@ class ExprVisitor(typing.Generic[_T_co]):
55
55
  def visit_cast(self, node: expr.Cast, /) -> _T_co: # pragma: no cover
56
56
  return self.visit_generic(node)
57
57
 
58
+ def visit_index(self, node: expr.Index, /) -> _T_co: # pragma: no cover
59
+ return self.visit_generic(node)
60
+
58
61
 
59
62
  class _VarWalkerImpl(ExprVisitor[typing.Iterable[expr.Var]]):
60
63
  __slots__ = ()
@@ -75,6 +78,10 @@ class _VarWalkerImpl(ExprVisitor[typing.Iterable[expr.Var]]):
75
78
  def visit_cast(self, node, /):
76
79
  yield from node.operand.accept(self)
77
80
 
81
+ def visit_index(self, node, /):
82
+ yield from node.target.accept(self)
83
+ yield from node.index.accept(self)
84
+
78
85
 
79
86
  _VAR_WALKER = _VarWalkerImpl()
80
87
 
@@ -164,6 +171,16 @@ class _StructuralEquivalenceImpl(ExprVisitor[bool]):
164
171
  self.other = self.other.operand
165
172
  return node.operand.accept(self)
166
173
 
174
+ def visit_index(self, node, /):
175
+ if self.other.__class__ is not node.__class__ or self.other.type != node.type:
176
+ return False
177
+ other = self.other
178
+ self.other = other.target
179
+ if not node.target.accept(self):
180
+ return False
181
+ self.other = other.index
182
+ return node.index.accept(self)
183
+
167
184
 
168
185
  def structurally_equivalent(
169
186
  left: expr.Expr,
@@ -215,3 +232,69 @@ def structurally_equivalent(
215
232
  True
216
233
  """
217
234
  return left.accept(_StructuralEquivalenceImpl(right, left_var_key, right_var_key))
235
+
236
+
237
+ class _IsLValueImpl(ExprVisitor[bool]):
238
+ __slots__ = ()
239
+
240
+ def visit_var(self, node, /):
241
+ return True
242
+
243
+ def visit_value(self, node, /):
244
+ return False
245
+
246
+ def visit_unary(self, node, /):
247
+ return False
248
+
249
+ def visit_binary(self, node, /):
250
+ return False
251
+
252
+ def visit_cast(self, node, /):
253
+ return False
254
+
255
+ def visit_index(self, node, /):
256
+ return node.target.accept(self)
257
+
258
+
259
+ _IS_LVALUE = _IsLValueImpl()
260
+
261
+
262
+ def is_lvalue(node: expr.Expr, /) -> bool:
263
+ """Return whether this expression can be used in l-value positions, that is, whether it has a
264
+ well-defined location in memory, such as one that might be writeable.
265
+
266
+ Being an l-value is a necessary but not sufficient for this location to be writeable; it is
267
+ permissible that a larger object containing this memory location may not allow writing from
268
+ the scope that attempts to write to it. This would be an access property of the containing
269
+ program, however, and not an inherent property of the expression system.
270
+
271
+ Examples:
272
+ Literal values are never l-values; there's no memory location associated with (for example)
273
+ the constant ``1``::
274
+
275
+ >>> from qiskit.circuit.classical import expr
276
+ >>> expr.is_lvalue(expr.lift(2))
277
+ False
278
+
279
+ :class:`~.expr.Var` nodes are always l-values, because they always have some associated
280
+ memory location::
281
+
282
+ >>> from qiskit.circuit.classical import types
283
+ >>> from qiskit.circuit import Clbit
284
+ >>> expr.is_lvalue(expr.Var.new("a", types.Bool()))
285
+ True
286
+ >>> expr.is_lvalue(expr.lift(Clbit()))
287
+ True
288
+
289
+ Currently there are no unary or binary operations on variables that can produce an l-value
290
+ expression, but it is likely in the future that some sort of "indexing" operation will be
291
+ added, which could produce l-values::
292
+
293
+ >>> a = expr.Var.new("a", types.Uint(8))
294
+ >>> b = expr.Var.new("b", types.Uint(8))
295
+ >>> expr.is_lvalue(a) and expr.is_lvalue(b)
296
+ True
297
+ >>> expr.is_lvalue(expr.bit_and(a, b))
298
+ False
299
+ """
300
+ return node.accept(_IS_LVALUE)
@@ -47,13 +47,14 @@ type, which may be slightly different to the 'classical' programming languages y
47
47
  Working with types
48
48
  ==================
49
49
 
50
- There are some functions on these types exposed here as well. These are mostly expected to be used
51
- only in manipulations of the expression tree; users who are building expressions using the
50
+ There are some additional functions on these types documented in the subsequent sections.
51
+ These are mostly expected to be used only in manipulations of the expression tree;
52
+ users who are building expressions using the
52
53
  :ref:`user-facing construction interface <circuit-classical-expressions-expr-construction>` should
53
54
  not need to use these.
54
55
 
55
56
  Partial ordering of types
56
- -------------------------
57
+ =========================
57
58
 
58
59
  The type system is equipped with a partial ordering, where :math:`a < b` is interpreted as
59
60
  ":math:`a` is a strict subtype of :math:`b`". Note that the partial ordering is a subset of the
@@ -78,7 +79,7 @@ Some helper methods are then defined in terms of this low-level :func:`order` pr
78
79
 
79
80
 
80
81
  Casting between types
81
- ---------------------
82
+ =====================
82
83
 
83
84
  It is common to need to cast values of one type to another type. The casting rules for this are
84
85
  embedded into the :mod:`types` module. You can query the casting kinds using :func:`cast_kind`:
@@ -81,6 +81,7 @@ classical_function
81
81
 
82
82
  Decorator for a classical function that returns a `ClassicalFunction` object.
83
83
 
84
+ .. autofunction:: classical_function
84
85
 
85
86
  ClassicalFunction
86
87
  -----------------
@@ -16,11 +16,15 @@ from functools import lru_cache
16
16
  from typing import List, Union
17
17
  import numpy as np
18
18
 
19
+ from qiskit import QiskitError
19
20
  from qiskit.circuit import Qubit
20
21
  from qiskit.circuit.operation import Operation
21
- from qiskit.circuit.controlflow import ControlFlowOp
22
+ from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES
22
23
  from qiskit.quantum_info.operators import Operator
23
24
 
25
+ _skipped_op_names = {"measure", "reset", "delay", "initialize"}
26
+ _no_cache_op_names = {"annotated"}
27
+
24
28
 
25
29
  @lru_cache(maxsize=None)
26
30
  def _identity_op(num_qubits):
@@ -94,8 +98,11 @@ class CommutationChecker:
94
98
  )
95
99
  first_op, first_qargs, _ = first_op_tuple
96
100
  second_op, second_qargs, _ = second_op_tuple
97
- first_params = first_op.params
98
- second_params = second_op.params
101
+
102
+ skip_cache = first_op.name in _no_cache_op_names or second_op.name in _no_cache_op_names
103
+
104
+ if skip_cache:
105
+ return _commute_matmul(first_op, first_qargs, second_op, second_qargs)
99
106
 
100
107
  commutation_lookup = self.check_commutation_entries(
101
108
  first_op, first_qargs, second_op, second_qargs
@@ -113,6 +120,8 @@ class CommutationChecker:
113
120
  if self._current_cache_entries >= self._cache_max_entries:
114
121
  self.clear_cached_commutations()
115
122
 
123
+ first_params = getattr(first_op, "params", [])
124
+ second_params = getattr(second_op, "params", [])
116
125
  if len(first_params) > 0 or len(second_params) > 0:
117
126
  self._cached_commutations.setdefault((first_op.name, second_op.name), {}).setdefault(
118
127
  _get_relative_placement(first_qargs, second_qargs), {}
@@ -184,7 +193,11 @@ class CommutationChecker:
184
193
 
185
194
 
186
195
  def _hashable_parameters(params):
187
- """Convert the parameters of a gate into a hashable format for lookup in a dictionary."""
196
+ """Convert the parameters of a gate into a hashable format for lookup in a dictionary.
197
+
198
+ This aims to be fast in common cases, and is not intended to work outside of the lifetime of a
199
+ single commutation pass; it does not handle mutable state correctly if the state is actually
200
+ changed."""
188
201
  try:
189
202
  hash(params)
190
203
  return params
@@ -201,7 +214,53 @@ def _hashable_parameters(params):
201
214
  return ("fallback", str(params))
202
215
 
203
216
 
204
- _skipped_op_names = {"measure", "reset", "delay"}
217
+ def is_commutation_supported(op):
218
+ """
219
+ Filter operations whose commutation is not supported due to bugs in transpiler passes invoking
220
+ commutation analysis.
221
+ Args:
222
+ op (Operation): operation to be checked for commutation relation
223
+ Return:
224
+ True if determining the commutation of op is currently supported
225
+ """
226
+ # Bug in CommutativeCancellation, e.g. see gh-8553
227
+ if getattr(op, "condition", False):
228
+ return False
229
+
230
+ # Commutation of ControlFlow gates also not supported yet. This may be pending a control flow graph.
231
+ if op.name in CONTROL_FLOW_OP_NAMES:
232
+ return False
233
+
234
+ return True
235
+
236
+
237
+ def is_commutation_skipped(op, qargs, max_num_qubits):
238
+ """
239
+ Filter operations whose commutation will not be determined.
240
+ Args:
241
+ op (Operation): operation to be checked for commutation relation
242
+ qargs (List): operation qubits
243
+ max_num_qubits (int): the maximum number of qubits to consider, the check may be skipped if
244
+ the number of qubits for either operation exceeds this amount.
245
+ Return:
246
+ True if determining the commutation of op is currently not supported
247
+ """
248
+ if (
249
+ len(qargs) > max_num_qubits
250
+ or getattr(op, "_directive", False)
251
+ or op.name in _skipped_op_names
252
+ ):
253
+ return True
254
+
255
+ if getattr(op, "is_parameterized", False) and op.is_parameterized():
256
+ return True
257
+
258
+ # we can proceed if op has defined: to_operator, to_matrix and __array__, or if its definition can be
259
+ # recursively resolved by operations that have a matrix. We check this by constructing an Operator.
260
+ if (hasattr(op, "to_matrix") and hasattr(op, "__array__")) or hasattr(op, "to_operator"):
261
+ return False
262
+
263
+ return False
205
264
 
206
265
 
207
266
  def _commutation_precheck(
@@ -213,43 +272,14 @@ def _commutation_precheck(
213
272
  cargs2: List,
214
273
  max_num_qubits,
215
274
  ):
216
- # pylint: disable=too-many-return-statements
217
-
218
- # We don't support commutation of conditional gates for now due to bugs in
219
- # CommutativeCancellation. See gh-8553.
220
- if getattr(op1, "condition", None) is not None or getattr(op2, "condition", None) is not None:
275
+ if not is_commutation_supported(op1) or not is_commutation_supported(op2):
221
276
  return False
222
277
 
223
- # Commutation of ControlFlow gates also not supported yet. This may be
224
- # pending a control flow graph.
225
- if isinstance(op1, ControlFlowOp) or isinstance(op2, ControlFlowOp):
226
- return False
227
-
228
- # These lines are adapted from dag_dependency and say that two gates over
229
- # different quantum and classical bits necessarily commute. This is more
230
- # permissive that the check from commutation_analysis, as for example it
231
- # allows to commute X(1) and Measure(0, 0).
232
- # Presumably this check was not present in commutation_analysis as
233
- # it was only called on pairs of connected nodes from DagCircuit.
234
- intersection_q = set(qargs1).intersection(set(qargs2))
235
- intersection_c = set(cargs1).intersection(set(cargs2))
236
- if not (intersection_q or intersection_c):
278
+ if set(qargs1).isdisjoint(qargs2) and set(cargs1).isdisjoint(cargs2):
237
279
  return True
238
280
 
239
- # Skip the check if the number of qubits for either operation is too large
240
- if len(qargs1) > max_num_qubits or len(qargs2) > max_num_qubits:
241
- return False
242
-
243
- # These lines are adapted from commutation_analysis, which is more restrictive than the
244
- # check from dag_dependency when considering nodes with "_directive". It would be nice to
245
- # think which optimizations from dag_dependency can indeed be used.
246
- if op1.name in _skipped_op_names or op2.name in _skipped_op_names:
247
- return False
248
-
249
- if getattr(op1, "_directive", False) or getattr(op2, "_directive", False):
250
- return False
251
- if (getattr(op1, "is_parameterized", False) and op1.is_parameterized()) or (
252
- getattr(op2, "is_parameterized", False) and op2.is_parameterized()
281
+ if is_commutation_skipped(op1, qargs1, max_num_qubits) or is_commutation_skipped(
282
+ op2, qargs2, max_num_qubits
253
283
  ):
254
284
  return False
255
285
 
@@ -264,13 +294,11 @@ def _get_relative_placement(first_qargs: List[Qubit], second_qargs: List[Qubit])
264
294
  second_qargs (DAGOpNode): second gate
265
295
 
266
296
  Return:
267
- A tuple that describes the relative qubit placement. The relative placement is defined by the
268
- gate qubit arrangements as q2^{-1}[q1[i]] where q1[i] is the ith qubit of the first gate and
269
- q2^{-1}[q] returns the qubit index of qubit q in the second gate (possibly 'None'). E.g.
297
+ A tuple that describes the relative qubit placement: E.g.
270
298
  _get_relative_placement(CX(0, 1), CX(1, 2)) would return (None, 0) as there is no overlap on
271
299
  the first qubit of the first gate but there is an overlap on the second qubit of the first gate,
272
- i.e. qubit 0 of the second gate. Likewise, _get_relative_placement(CX(1, 2), CX(0, 1)) would
273
- return (1, None)
300
+ i.e. qubit 0 of the second gate. Likewise,
301
+ _get_relative_placement(CX(1, 2), CX(0, 1)) would return (1, None)
274
302
  """
275
303
  qubits_g2 = {q_g1: i_g1 for i_g1, q_g1 in enumerate(second_qargs)}
276
304
  return tuple(qubits_g2.get(q_g0, None) for q_g0 in first_qargs)
@@ -285,7 +313,7 @@ def _persistent_id(op_name: str) -> int:
285
313
  Return:
286
314
  The integer id of the input string.
287
315
  """
288
- return int.from_bytes(bytes(op_name, encoding="ascii"), byteorder="big", signed=True)
316
+ return int.from_bytes(bytes(op_name, encoding="utf-8"), byteorder="big", signed=True)
289
317
 
290
318
 
291
319
  def _order_operations(
@@ -355,8 +383,10 @@ def _query_commutation(
355
383
  # if we have another dict in commutation_after_placement, commutation depends on params
356
384
  if isinstance(commutation_after_placement, dict):
357
385
  # Param commutation entry exists and must be a dict
386
+ first_params = getattr(first_op, "params", [])
387
+ second_params = getattr(second_op, "params", [])
358
388
  return commutation_after_placement.get(
359
- (_hashable_parameters(first_op.params), _hashable_parameters(second_op.params)),
389
+ (_hashable_parameters(first_params), _hashable_parameters(second_params)),
360
390
  None,
361
391
  )
362
392
  else:
@@ -379,12 +409,17 @@ def _commute_matmul(
379
409
  first_qarg = tuple(qarg[q] for q in first_qargs)
380
410
  second_qarg = tuple(qarg[q] for q in second_qargs)
381
411
 
382
- operator_1 = Operator(
383
- first_ops, input_dims=(2,) * len(first_qarg), output_dims=(2,) * len(first_qarg)
384
- )
385
- operator_2 = Operator(
386
- second_op, input_dims=(2,) * len(second_qarg), output_dims=(2,) * len(second_qarg)
387
- )
412
+ # try to generate an Operator out of op, if this succeeds we can determine commutativity, otherwise
413
+ # return false
414
+ try:
415
+ operator_1 = Operator(
416
+ first_ops, input_dims=(2,) * len(first_qarg), output_dims=(2,) * len(first_qarg)
417
+ )
418
+ operator_2 = Operator(
419
+ second_op, input_dims=(2,) * len(second_qarg), output_dims=(2,) * len(second_qarg)
420
+ )
421
+ except QiskitError:
422
+ return False
388
423
 
389
424
  if first_qarg == second_qarg:
390
425
  # Use full composition if possible to get the fastest matmul paths.
@@ -174,8 +174,16 @@ def _unify_circuit_resources_rebuild( # pylint: disable=invalid-name # (it's t
174
174
  out_circuits = []
175
175
  for circuit in circuits:
176
176
  out = QuantumCircuit(
177
- qubits, clbits, *circuit.qregs, *circuit.cregs, global_phase=circuit.global_phase
177
+ qubits,
178
+ clbits,
179
+ *circuit.qregs,
180
+ *circuit.cregs,
181
+ global_phase=circuit.global_phase,
182
+ inputs=circuit.iter_input_vars(),
183
+ captures=circuit.iter_captured_vars(),
178
184
  )
185
+ for var in circuit.iter_declared_vars():
186
+ out.add_uninitialized_var(var)
179
187
  for instruction in circuit.data:
180
188
  out._append(instruction)
181
189
  out_circuits.append(out)
@@ -19,31 +19,17 @@ from .builder import InstructionPlaceholder, InstructionResources
19
19
 
20
20
 
21
21
  class BreakLoopOp(Instruction):
22
- """A circuit operation which, when encountered, jumps to the end of
23
- the nearest enclosing loop.
24
-
25
- .. note:
26
-
27
- Can be inserted only within the body of a loop op, and must span
28
- the full width of that block.
29
-
30
- **Circuit symbol:**
31
-
32
- .. parsed-literal::
33
-
34
- ┌──────────────┐
35
- q_0: ┤0 ├
36
- │ │
37
- q_1: ┤1 ├
38
- │ break_loop │
39
- q_2: ┤2 ├
40
- │ │
41
- c_0: ╡0 ╞
42
- └──────────────┘
43
-
22
+ """A circuit operation which, when encountered, jumps to the end of the nearest enclosing loop.
23
+ Can only be used inside loops.
44
24
  """
45
25
 
46
26
  def __init__(self, num_qubits: int, num_clbits: int, label: Optional[str] = None):
27
+ """
28
+ Args:
29
+ num_qubits: the number of qubits this affects.
30
+ num_clbits: the number of qubits this affects.
31
+ label: an optional string label for the instruction.
32
+ """
47
33
  super().__init__("break_loop", num_qubits, num_clbits, [], label=label)
48
34
 
49
35
 
@@ -24,7 +24,8 @@ import itertools
24
24
  import typing
25
25
  from typing import Collection, Iterable, FrozenSet, Tuple, Union, Optional, Sequence
26
26
 
27
- from qiskit._accelerate.quantum_circuit import CircuitData
27
+ from qiskit._accelerate.circuit import CircuitData
28
+ from qiskit.circuit.classical import expr
28
29
  from qiskit.circuit.classicalregister import Clbit, ClassicalRegister
29
30
  from qiskit.circuit.exceptions import CircuitError
30
31
  from qiskit.circuit.instruction import Instruction
@@ -103,6 +104,66 @@ class CircuitScopeInterface(abc.ABC):
103
104
  or a :class:`.Clbit` that isn't actually in the circuit.
104
105
  """
105
106
 
107
+ @abc.abstractmethod
108
+ def add_uninitialized_var(self, var: expr.Var):
109
+ """Add an uninitialized variable to the circuit scope.
110
+
111
+ The general circuit context is responsible for ensuring the variable is initialized. These
112
+ uninitialized variables are guaranteed to be standalone.
113
+
114
+ Args:
115
+ var: the variable to add, if valid.
116
+
117
+ Raises:
118
+ CircuitError: if the variable cannot be added, such as because it invalidly shadows or
119
+ redefines an existing name.
120
+ """
121
+
122
+ @abc.abstractmethod
123
+ def remove_var(self, var: expr.Var):
124
+ """Remove a variable from the locals of this scope.
125
+
126
+ This is only called in the case that an exception occurred while initializing the variable,
127
+ and is not exposed to users.
128
+
129
+ Args:
130
+ var: the variable to remove. It can be assumed that this was already the subject of an
131
+ :meth:`add_uninitialized_var` call.
132
+ """
133
+
134
+ @abc.abstractmethod
135
+ def use_var(self, var: expr.Var):
136
+ """Called for every standalone classical real-time variable being used by some circuit
137
+ instruction.
138
+
139
+ The given variable is guaranteed to be a stand-alone variable; bit-like resource-wrapping
140
+ variables will have been filtered out and their resources given to
141
+ :meth:`resolve_classical_resource`.
142
+
143
+ Args:
144
+ var: the variable to validate.
145
+
146
+ Returns:
147
+ the same variable.
148
+
149
+ Raises:
150
+ CircuitError: if the variable is not valid for this scope.
151
+ """
152
+
153
+ @abc.abstractmethod
154
+ def get_var(self, name: str) -> Optional[expr.Var]:
155
+ """Get the variable (if any) in scope with the given name.
156
+
157
+ This should call up to the parent scope if in a control-flow builder scope, in case the
158
+ variable exists in an outer scope.
159
+
160
+ Args:
161
+ name: the name of the symbol to lookup.
162
+
163
+ Returns:
164
+ the variable if it is found, otherwise ``None``.
165
+ """
166
+
106
167
 
107
168
  class InstructionResources(typing.NamedTuple):
108
169
  """The quantum and classical resources used within a particular instruction.
@@ -271,6 +332,8 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
271
332
  "_parent",
272
333
  "_built",
273
334
  "_forbidden_message",
335
+ "_vars_local",
336
+ "_vars_capture",
274
337
  )
275
338
 
276
339
  def __init__(
@@ -311,6 +374,8 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
311
374
  self._instructions = CircuitData(qubits, clbits)
312
375
  self.registers = set(registers)
313
376
  self.global_phase = 0.0
377
+ self._vars_local = {}
378
+ self._vars_capture = {}
314
379
  self._allow_jumps = allow_jumps
315
380
  self._parent = parent
316
381
  self._built = False
@@ -393,6 +458,49 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
393
458
  self.add_register(resource)
394
459
  return resource
395
460
 
461
+ def add_uninitialized_var(self, var: expr.Var):
462
+ if self._built:
463
+ raise CircuitError("Cannot add resources after the scope has been built.")
464
+ # We can shadow a name if it was declared in an outer scope, but only if we haven't already
465
+ # captured it ourselves yet.
466
+ if (previous := self._vars_local.get(var.name)) is not None:
467
+ if previous == var:
468
+ raise CircuitError(f"'{var}' is already present in the scope")
469
+ raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'")
470
+ if var.name in self._vars_capture:
471
+ raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'")
472
+ self._vars_local[var.name] = var
473
+
474
+ def remove_var(self, var: expr.Var):
475
+ if self._built:
476
+ raise RuntimeError("exception handler 'remove_var' called after scope built")
477
+ self._vars_local.pop(var.name)
478
+
479
+ def get_var(self, name: str):
480
+ if (out := self._vars_local.get(name)) is not None:
481
+ return out
482
+ return self._parent.get_var(name)
483
+
484
+ def use_var(self, var: expr.Var):
485
+ if (local := self._vars_local.get(var.name)) is not None:
486
+ if local == var:
487
+ return
488
+ raise CircuitError(f"cannot use '{var}' which is shadowed by the local '{local}'")
489
+ if self._vars_capture.get(var.name) == var:
490
+ return
491
+ if self._parent.get_var(var.name) != var:
492
+ raise CircuitError(f"cannot close over '{var}', which is not in scope")
493
+ self._parent.use_var(var)
494
+ self._vars_capture[var.name] = var
495
+
496
+ def iter_local_vars(self):
497
+ """Iterator over the variables currently declared in this scope."""
498
+ return self._vars_local.values()
499
+
500
+ def iter_captured_vars(self):
501
+ """Iterator over the variables currently captured in this scope."""
502
+ return self._vars_capture.values()
503
+
396
504
  def peek(self) -> CircuitInstruction:
397
505
  """Get the value of the most recent instruction tuple in this scope."""
398
506
  if not self._instructions:
@@ -493,7 +601,12 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
493
601
  self._instructions.clbits,
494
602
  *self.registers,
495
603
  global_phase=self.global_phase,
604
+ captures=self._vars_capture.values(),
496
605
  )
606
+ for var in self._vars_local.values():
607
+ # The requisite `Store` instruction to initialise the variable will have been appended
608
+ # into the instructions.
609
+ out.add_uninitialized_var(var)
497
610
 
498
611
  # Maps placeholder index to the newly concrete instruction.
499
612
  placeholder_to_concrete = {}
@@ -566,6 +679,8 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
566
679
  out._instructions = self._instructions.copy()
567
680
  out.registers = self.registers.copy()
568
681
  out.global_phase = self.global_phase
682
+ out._vars_local = self._vars_local.copy()
683
+ out._vars_capture = self._vars_capture.copy()
569
684
  out._parent = self._parent
570
685
  out._allow_jumps = self._allow_jumps
571
686
  out._forbidden_message = self._forbidden_message
@@ -19,31 +19,17 @@ from .builder import InstructionPlaceholder, InstructionResources
19
19
 
20
20
 
21
21
  class ContinueLoopOp(Instruction):
22
- """A circuit operation which, when encountered, moves to the next iteration of
23
- the nearest enclosing loop.
24
-
25
- .. note::
26
-
27
- Can be inserted only within the body of a loop op, and must span the full
28
- width of that block.
29
-
30
- **Circuit symbol:**
31
-
32
- .. parsed-literal::
33
-
34
- ┌─────────────────┐
35
- q_0: ┤0 ├
36
- │ │
37
- q_1: ┤1 ├
38
- │ continue_loop │
39
- q_2: ┤2 ├
40
- │ │
41
- c_0: ╡0 ╞
42
- └─────────────────┘
43
-
22
+ """A circuit operation which, when encountered, moves to the next iteration of the nearest
23
+ enclosing loop. Can only be used inside loops.
44
24
  """
45
25
 
46
26
  def __init__(self, num_qubits: int, num_clbits: int, label: Optional[str] = None):
27
+ """
28
+ Args:
29
+ num_qubits: the number of qubits this affects.
30
+ num_clbits: the number of qubits this affects.
31
+ label: an optional string label for the instruction.
32
+ """
47
33
  super().__init__("continue_loop", num_qubits, num_clbits, [], label=label)
48
34
 
49
35