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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +27 -16
  3. qiskit/_accelerate.abi3.so +0 -0
  4. qiskit/_numpy_compat.py +73 -0
  5. qiskit/assembler/disassemble.py +5 -6
  6. qiskit/circuit/__init__.py +1131 -169
  7. qiskit/circuit/_classical_resource_map.py +7 -6
  8. qiskit/circuit/_utils.py +18 -8
  9. qiskit/circuit/annotated_operation.py +21 -0
  10. qiskit/circuit/barrier.py +10 -13
  11. qiskit/circuit/bit.py +0 -1
  12. qiskit/circuit/classical/__init__.py +2 -2
  13. qiskit/circuit/classical/expr/__init__.py +39 -5
  14. qiskit/circuit/classical/expr/constructors.py +84 -1
  15. qiskit/circuit/classical/expr/expr.py +83 -13
  16. qiskit/circuit/classical/expr/visitors.py +83 -0
  17. qiskit/circuit/commutation_checker.py +86 -51
  18. qiskit/circuit/controlflow/_builder_utils.py +9 -1
  19. qiskit/circuit/controlflow/break_loop.py +8 -22
  20. qiskit/circuit/controlflow/builder.py +116 -1
  21. qiskit/circuit/controlflow/continue_loop.py +8 -22
  22. qiskit/circuit/controlflow/control_flow.py +47 -8
  23. qiskit/circuit/controlflow/for_loop.py +8 -23
  24. qiskit/circuit/controlflow/if_else.py +13 -27
  25. qiskit/circuit/controlflow/switch_case.py +14 -21
  26. qiskit/circuit/controlflow/while_loop.py +9 -23
  27. qiskit/circuit/controlledgate.py +2 -2
  28. qiskit/circuit/delay.py +7 -5
  29. qiskit/circuit/gate.py +20 -7
  30. qiskit/circuit/instruction.py +31 -30
  31. qiskit/circuit/instructionset.py +9 -22
  32. qiskit/circuit/library/__init__.py +8 -2
  33. qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
  34. qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
  35. qiskit/circuit/library/blueprintcircuit.py +29 -7
  36. qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
  37. qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
  38. qiskit/circuit/library/generalized_gates/isometry.py +51 -254
  39. qiskit/circuit/library/generalized_gates/pauli.py +2 -2
  40. qiskit/circuit/library/generalized_gates/permutation.py +4 -1
  41. qiskit/circuit/library/generalized_gates/rv.py +15 -11
  42. qiskit/circuit/library/generalized_gates/uc.py +2 -98
  43. qiskit/circuit/library/generalized_gates/unitary.py +9 -4
  44. qiskit/circuit/library/hamiltonian_gate.py +11 -5
  45. qiskit/circuit/library/n_local/efficient_su2.py +5 -5
  46. qiskit/circuit/library/n_local/n_local.py +100 -49
  47. qiskit/circuit/library/n_local/two_local.py +3 -59
  48. qiskit/circuit/library/overlap.py +3 -3
  49. qiskit/circuit/library/phase_oracle.py +1 -1
  50. qiskit/circuit/library/quantum_volume.py +39 -38
  51. qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
  52. qiskit/circuit/library/standard_gates/global_phase.py +4 -2
  53. qiskit/circuit/library/standard_gates/i.py +1 -2
  54. qiskit/circuit/library/standard_gates/iswap.py +1 -2
  55. qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
  56. qiskit/circuit/library/standard_gates/p.py +31 -15
  57. qiskit/circuit/library/standard_gates/r.py +4 -3
  58. qiskit/circuit/library/standard_gates/rx.py +7 -4
  59. qiskit/circuit/library/standard_gates/rxx.py +4 -3
  60. qiskit/circuit/library/standard_gates/ry.py +7 -4
  61. qiskit/circuit/library/standard_gates/ryy.py +4 -3
  62. qiskit/circuit/library/standard_gates/rz.py +7 -4
  63. qiskit/circuit/library/standard_gates/rzx.py +4 -3
  64. qiskit/circuit/library/standard_gates/rzz.py +4 -3
  65. qiskit/circuit/library/standard_gates/s.py +4 -8
  66. qiskit/circuit/library/standard_gates/t.py +2 -4
  67. qiskit/circuit/library/standard_gates/u.py +16 -11
  68. qiskit/circuit/library/standard_gates/u1.py +6 -2
  69. qiskit/circuit/library/standard_gates/u2.py +4 -2
  70. qiskit/circuit/library/standard_gates/u3.py +9 -5
  71. qiskit/circuit/library/standard_gates/x.py +22 -11
  72. qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
  73. qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
  74. qiskit/circuit/library/standard_gates/z.py +1 -2
  75. qiskit/circuit/measure.py +4 -1
  76. qiskit/circuit/operation.py +13 -8
  77. qiskit/circuit/parameter.py +11 -6
  78. qiskit/circuit/quantumcircuit.py +864 -128
  79. qiskit/circuit/quantumcircuitdata.py +2 -2
  80. qiskit/circuit/reset.py +5 -2
  81. qiskit/circuit/store.py +95 -0
  82. qiskit/compiler/assembler.py +22 -22
  83. qiskit/compiler/transpiler.py +63 -112
  84. qiskit/converters/circuit_to_dag.py +7 -0
  85. qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
  86. qiskit/converters/circuit_to_gate.py +2 -0
  87. qiskit/converters/circuit_to_instruction.py +22 -0
  88. qiskit/converters/dag_to_circuit.py +4 -0
  89. qiskit/converters/dag_to_dagdependency_v2.py +44 -0
  90. qiskit/dagcircuit/collect_blocks.py +15 -10
  91. qiskit/dagcircuit/dagcircuit.py +434 -124
  92. qiskit/dagcircuit/dagdependency.py +19 -12
  93. qiskit/dagcircuit/dagdependency_v2.py +641 -0
  94. qiskit/dagcircuit/dagdepnode.py +19 -16
  95. qiskit/dagcircuit/dagnode.py +14 -4
  96. qiskit/primitives/__init__.py +12 -8
  97. qiskit/primitives/backend_estimator.py +3 -5
  98. qiskit/primitives/backend_estimator_v2.py +410 -0
  99. qiskit/primitives/backend_sampler_v2.py +287 -0
  100. qiskit/primitives/base/base_estimator.py +4 -9
  101. qiskit/primitives/base/base_sampler.py +2 -2
  102. qiskit/primitives/containers/__init__.py +5 -4
  103. qiskit/primitives/containers/bit_array.py +292 -2
  104. qiskit/primitives/containers/data_bin.py +123 -50
  105. qiskit/primitives/containers/estimator_pub.py +10 -3
  106. qiskit/primitives/containers/observables_array.py +2 -2
  107. qiskit/primitives/containers/pub_result.py +1 -1
  108. qiskit/primitives/containers/sampler_pub.py +19 -3
  109. qiskit/primitives/containers/sampler_pub_result.py +74 -0
  110. qiskit/primitives/containers/shape.py +1 -1
  111. qiskit/primitives/statevector_estimator.py +4 -4
  112. qiskit/primitives/statevector_sampler.py +7 -12
  113. qiskit/providers/__init__.py +17 -18
  114. qiskit/providers/backend.py +2 -2
  115. qiskit/providers/backend_compat.py +8 -10
  116. qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
  117. qiskit/providers/basic_provider/basic_simulator.py +81 -21
  118. qiskit/providers/fake_provider/fake_1q.py +1 -1
  119. qiskit/providers/fake_provider/fake_backend.py +3 -408
  120. qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
  121. qiskit/providers/provider.py +16 -0
  122. qiskit/pulse/builder.py +4 -1
  123. qiskit/pulse/parameter_manager.py +60 -4
  124. qiskit/pulse/schedule.py +29 -13
  125. qiskit/pulse/utils.py +61 -20
  126. qiskit/qasm2/__init__.py +1 -5
  127. qiskit/qasm2/parse.py +1 -4
  128. qiskit/qasm3/__init__.py +42 -5
  129. qiskit/qasm3/ast.py +19 -0
  130. qiskit/qasm3/exporter.py +178 -106
  131. qiskit/qasm3/printer.py +27 -5
  132. qiskit/qpy/__init__.py +247 -13
  133. qiskit/qpy/binary_io/circuits.py +216 -47
  134. qiskit/qpy/binary_io/schedules.py +42 -36
  135. qiskit/qpy/binary_io/value.py +201 -22
  136. qiskit/qpy/common.py +1 -1
  137. qiskit/qpy/exceptions.py +20 -0
  138. qiskit/qpy/formats.py +29 -0
  139. qiskit/qpy/type_keys.py +21 -0
  140. qiskit/quantum_info/analysis/distance.py +3 -3
  141. qiskit/quantum_info/analysis/make_observable.py +2 -1
  142. qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
  143. qiskit/quantum_info/operators/channel/chi.py +9 -8
  144. qiskit/quantum_info/operators/channel/choi.py +10 -9
  145. qiskit/quantum_info/operators/channel/kraus.py +2 -1
  146. qiskit/quantum_info/operators/channel/ptm.py +10 -9
  147. qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
  148. qiskit/quantum_info/operators/channel/stinespring.py +2 -1
  149. qiskit/quantum_info/operators/channel/superop.py +12 -11
  150. qiskit/quantum_info/operators/channel/transformations.py +12 -11
  151. qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
  152. qiskit/quantum_info/operators/operator.py +43 -30
  153. qiskit/quantum_info/operators/scalar_op.py +10 -9
  154. qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
  155. qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
  156. qiskit/quantum_info/operators/symplectic/pauli.py +48 -4
  157. qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
  158. qiskit/quantum_info/operators/symplectic/random.py +3 -2
  159. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +54 -33
  160. qiskit/quantum_info/states/densitymatrix.py +13 -13
  161. qiskit/quantum_info/states/stabilizerstate.py +3 -3
  162. qiskit/quantum_info/states/statevector.py +14 -13
  163. qiskit/quantum_info/states/utils.py +5 -3
  164. qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
  165. qiskit/result/mitigation/local_readout_mitigator.py +2 -1
  166. qiskit/result/mitigation/utils.py +3 -2
  167. qiskit/synthesis/__init__.py +2 -0
  168. qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
  169. qiskit/synthesis/evolution/lie_trotter.py +7 -14
  170. qiskit/synthesis/evolution/qdrift.py +3 -4
  171. qiskit/synthesis/linear/cnot_synth.py +1 -3
  172. qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
  173. qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
  174. qiskit/synthesis/permutation/__init__.py +1 -0
  175. qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
  176. qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
  177. qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
  178. qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
  179. qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
  180. qiskit/synthesis/unitary/aqc/__init__.py +1 -1
  181. qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
  182. qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
  183. qiskit/synthesis/unitary/qsd.py +3 -2
  184. qiskit/transpiler/__init__.py +7 -3
  185. qiskit/transpiler/layout.py +140 -61
  186. qiskit/transpiler/passes/__init__.py +6 -0
  187. qiskit/transpiler/passes/basis/basis_translator.py +7 -2
  188. qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
  189. qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
  190. qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
  191. qiskit/transpiler/passes/layout/apply_layout.py +8 -3
  192. qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
  193. qiskit/transpiler/passes/layout/set_layout.py +1 -1
  194. qiskit/transpiler/passes/optimization/__init__.py +2 -0
  195. qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
  196. qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
  197. qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
  198. qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
  199. qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
  200. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
  201. qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
  202. qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
  203. qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
  204. qiskit/transpiler/passes/routing/__init__.py +1 -0
  205. qiskit/transpiler/passes/routing/basic_swap.py +13 -2
  206. qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
  207. qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
  208. qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
  209. qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
  210. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  211. qiskit/transpiler/passes/scheduling/alap.py +1 -2
  212. qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
  213. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
  214. qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
  215. qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
  216. qiskit/transpiler/passes/scheduling/asap.py +1 -2
  217. qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
  218. qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
  219. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
  220. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
  221. qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
  222. qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
  223. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
  224. qiskit/transpiler/passes/utils/gates_basis.py +3 -3
  225. qiskit/transpiler/passmanager.py +44 -1
  226. qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
  227. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
  228. qiskit/transpiler/preset_passmanagers/common.py +4 -6
  229. qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
  230. qiskit/utils/optionals.py +6 -2
  231. qiskit/visualization/array.py +1 -1
  232. qiskit/visualization/bloch.py +2 -3
  233. qiskit/visualization/circuit/matplotlib.py +44 -14
  234. qiskit/visualization/circuit/text.py +38 -18
  235. qiskit/visualization/counts_visualization.py +3 -6
  236. qiskit/visualization/dag_visualization.py +6 -7
  237. qiskit/visualization/pulse_v2/interface.py +8 -3
  238. qiskit/visualization/state_visualization.py +3 -2
  239. qiskit/visualization/timeline/interface.py +18 -8
  240. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/METADATA +12 -8
  241. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/RECORD +245 -235
  242. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/WHEEL +1 -1
  243. qiskit/_qasm2.abi3.so +0 -0
  244. qiskit/_qasm3.abi3.so +0 -0
  245. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/LICENSE.txt +0 -0
  246. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/entry_points.txt +0 -0
  247. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -37,17 +37,20 @@ class VariableMapper(expr.ExprVisitor[expr.Expr]):
37
37
  ``ValueError`` will be raised instead. The given ``add_register`` callable may choose to raise
38
38
  its own exception."""
39
39
 
40
- __slots__ = ("target_cregs", "register_map", "bit_map", "add_register")
40
+ __slots__ = ("target_cregs", "register_map", "bit_map", "var_map", "add_register")
41
41
 
42
42
  def __init__(
43
43
  self,
44
44
  target_cregs: typing.Iterable[ClassicalRegister],
45
45
  bit_map: typing.Mapping[Bit, Bit],
46
+ var_map: typing.Mapping[expr.Var, expr.Var] | None = None,
47
+ *,
46
48
  add_register: typing.Callable[[ClassicalRegister], None] | None = None,
47
49
  ):
48
50
  self.target_cregs = tuple(target_cregs)
49
51
  self.register_map = {}
50
52
  self.bit_map = bit_map
53
+ self.var_map = var_map or {}
51
54
  self.add_register = add_register
52
55
 
53
56
  def _map_register(self, theirs: ClassicalRegister) -> ClassicalRegister:
@@ -110,8 +113,8 @@ class VariableMapper(expr.ExprVisitor[expr.Expr]):
110
113
  return (mapped_theirs, mapped_value)
111
114
 
112
115
  def map_target(self, target, /):
113
- """Map the runtime variables in a ``target`` of a :class:`.SwitchCaseOp` to the new circuit,
114
- as defined in the ``circuit`` argument of the initialiser of this class."""
116
+ """Map the real-time variables in a ``target`` of a :class:`.SwitchCaseOp` to the new
117
+ circuit, as defined in the ``circuit`` argument of the initialiser of this class."""
115
118
  if isinstance(target, Clbit):
116
119
  return self.bit_map[target]
117
120
  if isinstance(target, ClassicalRegister):
@@ -127,9 +130,7 @@ class VariableMapper(expr.ExprVisitor[expr.Expr]):
127
130
  return expr.Var(self.bit_map[node.var], node.type)
128
131
  if isinstance(node.var, ClassicalRegister):
129
132
  return expr.Var(self._map_register(node.var), node.type)
130
- # Defensive against the expansion of the variable system; we don't want to silently do the
131
- # wrong thing (which would be `return node` without mapping, right now).
132
- raise RuntimeError(f"unhandled variable in 'compose': {node}") # pragma: no cover
133
+ return self.var_map.get(node, node)
133
134
 
134
135
  def visit_value(self, node, /):
135
136
  return expr.Value(node.value, node.type)
qiskit/circuit/_utils.py CHANGED
@@ -13,7 +13,10 @@
13
13
  This module contains utility functions for circuits.
14
14
  """
15
15
 
16
+ import math
16
17
  import numpy
18
+
19
+ from qiskit import _numpy_compat
17
20
  from qiskit.exceptions import QiskitError
18
21
  from qiskit.circuit.exceptions import CircuitError
19
22
  from .parametervector import ParameterVectorElement
@@ -56,7 +59,7 @@ def _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None):
56
59
  Raises:
57
60
  QiskitError: unrecognized mode or invalid ctrl_state
58
61
  """
59
- num_target = int(numpy.log2(base_mat.shape[0]))
62
+ num_target = int(math.log2(base_mat.shape[0]))
60
63
  ctrl_dim = 2**num_ctrl_qubits
61
64
  ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim - 1])
62
65
  if ctrl_state is None:
@@ -116,8 +119,9 @@ def with_gate_array(base_array):
116
119
  nonwritable = numpy.array(base_array, dtype=numpy.complex128)
117
120
  nonwritable.setflags(write=False)
118
121
 
119
- def __array__(_self, dtype=None):
120
- return numpy.asarray(nonwritable, dtype=dtype)
122
+ def __array__(_self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
123
+ dtype = nonwritable.dtype if dtype is None else dtype
124
+ return numpy.array(nonwritable, dtype=dtype, copy=copy)
121
125
 
122
126
  def decorator(cls):
123
127
  if hasattr(cls, "__array__"):
@@ -148,15 +152,21 @@ def with_controlled_gate_array(base_array, num_ctrl_qubits, cached_states=None):
148
152
  if cached_states is None:
149
153
  nonwritables = [matrix_for_control_state(state) for state in range(2**num_ctrl_qubits)]
150
154
 
151
- def __array__(self, dtype=None):
152
- return numpy.asarray(nonwritables[self.ctrl_state], dtype=dtype)
155
+ def __array__(self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
156
+ arr = nonwritables[self.ctrl_state]
157
+ dtype = arr.dtype if dtype is None else dtype
158
+ return numpy.array(arr, dtype=dtype, copy=copy)
153
159
 
154
160
  else:
155
161
  nonwritables = {state: matrix_for_control_state(state) for state in cached_states}
156
162
 
157
- def __array__(self, dtype=None):
158
- if (out := nonwritables.get(self.ctrl_state)) is not None:
159
- return numpy.asarray(out, dtype=dtype)
163
+ def __array__(self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
164
+ if (arr := nonwritables.get(self.ctrl_state)) is not None:
165
+ dtype = arr.dtype if dtype is None else dtype
166
+ return numpy.array(arr, dtype=dtype, copy=copy)
167
+
168
+ if copy is False and copy is not _numpy_compat.COPY_ONLY_IF_NEEDED:
169
+ raise ValueError("could not produce matrix without calculation")
160
170
  return numpy.asarray(
161
171
  _compute_control_matrix(base, num_ctrl_qubits, self.ctrl_state), dtype=dtype
162
172
  )
@@ -97,7 +97,10 @@ class AnnotatedOperation(Operation):
97
97
  inverted and then controlled by 2 qubits.
98
98
  """
99
99
  self.base_op = base_op
100
+ """The base operation that the modifiers in this annotated operation applies to."""
100
101
  self.modifiers = modifiers if isinstance(modifiers, List) else [modifiers]
102
+ """Ordered sequence of the modifiers to apply to :attr:`base_op`. The modifiers are applied
103
+ in order from lowest index to highest index."""
101
104
 
102
105
  @property
103
106
  def name(self):
@@ -198,6 +201,24 @@ class AnnotatedOperation(Operation):
198
201
  extended_modifiers.append(InverseModifier())
199
202
  return AnnotatedOperation(self.base_op, extended_modifiers)
200
203
 
204
+ def power(self, exponent: float, annotated: bool = False):
205
+ """
206
+ Raise this gate to the power of ``exponent``.
207
+
208
+ Implemented as an annotated operation, see :class:`.AnnotatedOperation`.
209
+
210
+ Args:
211
+ exponent: the power to raise the gate to
212
+ annotated: ignored (used for consistency with other power methods)
213
+
214
+ Returns:
215
+ An operation implementing ``gate^exponent``
216
+ """
217
+ # pylint: disable=unused-argument
218
+ extended_modifiers = self.modifiers.copy()
219
+ extended_modifiers.append(PowerModifier(exponent))
220
+ return AnnotatedOperation(self.base_op, extended_modifiers)
221
+
201
222
 
202
223
  def _canonicalize_modifiers(modifiers):
203
224
  """
qiskit/circuit/barrier.py CHANGED
@@ -16,29 +16,26 @@ Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
16
16
  with the :meth:`~qiskit.circuit.QuantumCircuit.barrier` method.
17
17
  """
18
18
 
19
+ from __future__ import annotations
20
+
19
21
  from qiskit.exceptions import QiskitError
20
22
  from .instruction import Instruction
21
23
 
22
24
 
23
25
  class Barrier(Instruction):
24
- """Barrier instruction.
26
+ """A directive for circuit compilation to separate pieces of a circuit so that any optimizations
27
+ or re-writes are constrained to only act between barriers.
25
28
 
26
- A barrier is a visual indicator of the grouping of a circuit section.
27
- It also acts as a directive for circuit compilation to separate pieces
28
- of a circuit so that any optimizations or re-writes are constrained
29
- to only act between barriers."""
29
+ This will also appear in visualizations as a visual marker.
30
+ """
30
31
 
31
32
  _directive = True
32
33
 
33
- def __init__(self, num_qubits, label=None):
34
- """Create new barrier instruction.
35
-
34
+ def __init__(self, num_qubits: int, label: str | None = None):
35
+ """
36
36
  Args:
37
- num_qubits (int): the number of qubits for the barrier type [Default: 0].
38
- label (str): the barrier label
39
-
40
- Raises:
41
- TypeError: if barrier label is invalid.
37
+ num_qubits: the number of qubits for the barrier.
38
+ label: the optional label of this barrier.
42
39
  """
43
40
  self._label = label
44
41
  super().__init__("barrier", num_qubits, 0, [], label=label)
qiskit/circuit/bit.py CHANGED
@@ -24,7 +24,6 @@ class Bit:
24
24
  .. note::
25
25
  This class should not be instantiated directly. This is just a superclass
26
26
  for :class:`~.Clbit` and :class:`~.circuit.Qubit`.
27
-
28
27
  """
29
28
 
30
29
  __slots__ = {"_register", "_index", "_hash", "_repr"}
@@ -15,8 +15,8 @@
15
15
  Classical expressions (:mod:`qiskit.circuit.classical`)
16
16
  =======================================================
17
17
 
18
- This module contains an exploratory representation of runtime operations on classical values during
19
- circuit execution.
18
+ This module contains an exploratory representation of real-time operations on classical values
19
+ during circuit execution.
20
20
 
21
21
  Currently, only simple expressions on bits and registers that result in a Boolean value are
22
22
  supported, and these are only valid for use in the conditions of :meth:`.QuantumCircuit.if_test`
@@ -39,12 +39,13 @@ The expression system is based on tree representation. All nodes in the tree ar
39
39
 
40
40
  These objects are mutable and should not be reused in a different location without a copy.
41
41
 
42
- The entry point from general circuit objects to the expression system is by wrapping the object
43
- in a :class:`Var` node and associating a :class:`~.types.Type` with it.
42
+ The base for dynamic variables is the :class:`Var`, which can be either an arbitrarily typed
43
+ real-time variable, or a wrapper around a :class:`.Clbit` or :class:`.ClassicalRegister`.
44
44
 
45
45
  .. autoclass:: Var
46
+ :members: var, name
46
47
 
47
- Similarly, literals used in comparison (such as integers) should be lifted to :class:`Value` nodes
48
+ Similarly, literals used in expressions (such as integers) should be lifted to :class:`Value` nodes
48
49
  with associated types.
49
50
 
50
51
  .. autoclass:: Value
@@ -61,6 +62,12 @@ and :class:`Binary.Op` respectively.
61
62
  :members: Op
62
63
  :member-order: bysource
63
64
 
65
+ Bit-like types (unsigned integers) can be indexed by integer types, represented by :class:`Index`.
66
+ The result is a single bit. The resulting expression has an associated memory location (and so can
67
+ be used as an lvalue for :class:`.Store`, etc) if the target is also an lvalue.
68
+
69
+ .. autoclass:: Index
70
+
64
71
  When constructing expressions, one must ensure that the types are valid for the operation.
65
72
  Attempts to construct expressions with invalid types will raise a regular Python ``TypeError``.
66
73
 
@@ -91,6 +98,13 @@ some scalar value as an :class:`Expr` node, you can manually :func:`lift` it you
91
98
 
92
99
  .. autofunction:: lift
93
100
 
101
+ Typically you should create memory-owning :class:`Var` instances by using the
102
+ :meth:`.QuantumCircuit.add_var` method to declare them in some circuit context, since a
103
+ :class:`.QuantumCircuit` will not accept an :class:`Expr` that contains variables that are not
104
+ already declared in it, since it needs to know how to allocate the storage and how the variable will
105
+ be initialized. However, should you want to do this manually, you should use the low-level
106
+ :meth:`Var.new` call to safely generate a named variable for usage.
107
+
94
108
  You can manually specify casts in cases where the cast is allowed in explicit form, but may be
95
109
  lossy (such as the cast of a higher precision :class:`~.types.Uint` to a lower precision one).
96
110
 
@@ -114,6 +128,13 @@ Similarly, the binary operations and relations have helper functions defined.
114
128
  .. autofunction:: less_equal
115
129
  .. autofunction:: greater
116
130
  .. autofunction:: greater_equal
131
+ .. autofunction:: shift_left
132
+ .. autofunction:: shift_right
133
+
134
+ You can index into unsigned integers and bit-likes using another unsigned integer of any width.
135
+ This includes in storing operations, if the target of the index is writeable.
136
+
137
+ .. autofunction:: index
117
138
 
118
139
  Qiskit's legacy method for specifying equality conditions for use in conditionals is to use a
119
140
  two-tuple of a :class:`.Clbit` or :class:`.ClassicalRegister` and an integer. This represents an
@@ -152,6 +173,11 @@ between two different circuits. In this case, one can use :func:`structurally_e
152
173
  suitable "key" functions to do the comparison.
153
174
 
154
175
  .. autofunction:: structurally_equivalent
176
+
177
+ Some expressions have associated memory locations, and others may be purely temporary.
178
+ You can use :func:`is_lvalue` to determine whether an expression has an associated memory location.
179
+
180
+ .. autofunction:: is_lvalue
155
181
  """
156
182
 
157
183
  __all__ = [
@@ -161,9 +187,11 @@ __all__ = [
161
187
  "Cast",
162
188
  "Unary",
163
189
  "Binary",
190
+ "Index",
164
191
  "ExprVisitor",
165
192
  "iter_vars",
166
193
  "structurally_equivalent",
194
+ "is_lvalue",
167
195
  "lift",
168
196
  "cast",
169
197
  "bit_not",
@@ -171,6 +199,8 @@ __all__ = [
171
199
  "bit_and",
172
200
  "bit_or",
173
201
  "bit_xor",
202
+ "shift_left",
203
+ "shift_right",
174
204
  "logic_and",
175
205
  "logic_or",
176
206
  "equal",
@@ -179,11 +209,12 @@ __all__ = [
179
209
  "less_equal",
180
210
  "greater",
181
211
  "greater_equal",
212
+ "index",
182
213
  "lift_legacy_condition",
183
214
  ]
184
215
 
185
- from .expr import Expr, Var, Value, Cast, Unary, Binary
186
- from .visitors import ExprVisitor, iter_vars, structurally_equivalent
216
+ from .expr import Expr, Var, Value, Cast, Unary, Binary, Index
217
+ from .visitors import ExprVisitor, iter_vars, structurally_equivalent, is_lvalue
187
218
  from .constructors import (
188
219
  lift,
189
220
  cast,
@@ -200,5 +231,8 @@ from .constructors import (
200
231
  less_equal,
201
232
  greater,
202
233
  greater_equal,
234
+ shift_left,
235
+ shift_right,
236
+ index,
203
237
  lift_legacy_condition,
204
238
  )
@@ -37,7 +37,7 @@ __all__ = [
37
37
 
38
38
  import typing
39
39
 
40
- from .expr import Expr, Var, Value, Unary, Binary, Cast
40
+ from .expr import Expr, Var, Value, Unary, Binary, Cast, Index
41
41
  from ..types import CastKind, cast_kind
42
42
  from .. import types
43
43
 
@@ -471,3 +471,86 @@ Var(ClassicalRegister(3, "b"), Uint(3)), \
471
471
  Uint(3))
472
472
  """
473
473
  return _binary_relation(Binary.Op.GREATER_EQUAL, left, right)
474
+
475
+
476
+ def _shift_like(
477
+ op: Binary.Op, left: typing.Any, right: typing.Any, type: types.Type | None
478
+ ) -> Expr:
479
+ if type is not None and type.kind is not types.Uint:
480
+ raise TypeError(f"type '{type}' is not a valid bitshift operand type")
481
+ if isinstance(left, Expr):
482
+ left = _coerce_lossless(left, type) if type is not None else left
483
+ else:
484
+ left = lift(left, type)
485
+ right = lift(right)
486
+ if left.type.kind != types.Uint or right.type.kind != types.Uint:
487
+ raise TypeError(f"invalid types for '{op}': '{left.type}' and '{right.type}'")
488
+ return Binary(op, left, right, left.type)
489
+
490
+
491
+ def shift_left(left: typing.Any, right: typing.Any, /, type: types.Type | None = None) -> Expr:
492
+ """Create a 'bitshift left' expression node from the given two values, resolving any implicit
493
+ casts and lifting the values into :class:`Value` nodes if required.
494
+
495
+ If ``type`` is given, the ``left`` operand will be coerced to it (if possible).
496
+
497
+ Examples:
498
+ Shift the value of a standalone variable left by some amount::
499
+
500
+ >>> from qiskit.circuit.classical import expr, types
501
+ >>> a = expr.Var.new("a", types.Uint(8))
502
+ >>> expr.shift_left(a, 4)
503
+ Binary(Binary.Op.SHIFT_LEFT, \
504
+ Var(<UUID>, Uint(8), name='a'), \
505
+ Value(4, Uint(3)), \
506
+ Uint(8))
507
+
508
+ Shift an integer literal by a variable amount, coercing the type of the literal::
509
+
510
+ >>> expr.shift_left(3, a, types.Uint(16))
511
+ Binary(Binary.Op.SHIFT_LEFT, \
512
+ Value(3, Uint(16)), \
513
+ Var(<UUID>, Uint(8), name='a'), \
514
+ Uint(16))
515
+ """
516
+ return _shift_like(Binary.Op.SHIFT_LEFT, left, right, type)
517
+
518
+
519
+ def shift_right(left: typing.Any, right: typing.Any, /, type: types.Type | None = None) -> Expr:
520
+ """Create a 'bitshift right' expression node from the given values, resolving any implicit casts
521
+ and lifting the values into :class:`Value` nodes if required.
522
+
523
+ If ``type`` is given, the ``left`` operand will be coerced to it (if possible).
524
+
525
+ Examples:
526
+ Shift the value of a classical register right by some amount::
527
+
528
+ >>> from qiskit.circuit import ClassicalRegister
529
+ >>> from qiskit.circuit.classical import expr
530
+ >>> expr.shift_right(ClassicalRegister(8, "a"), 4)
531
+ Binary(Binary.Op.SHIFT_RIGHT, \
532
+ Var(ClassicalRegister(8, "a"), Uint(8)), \
533
+ Value(4, Uint(3)), \
534
+ Uint(8))
535
+ """
536
+ return _shift_like(Binary.Op.SHIFT_RIGHT, left, right, type)
537
+
538
+
539
+ def index(target: typing.Any, index: typing.Any, /) -> Expr:
540
+ """Index into the ``target`` with the given integer ``index``, lifting the values into
541
+ :class:`Value` nodes if required.
542
+
543
+ This can be used as the target of a :class:`.Store`, if the ``target`` is itself an lvalue.
544
+
545
+ Examples:
546
+ Index into a classical register with a literal::
547
+
548
+ >>> from qiskit.circuit import ClassicalRegister
549
+ >>> from qiskit.circuit.classical import expr
550
+ >>> expr.index(ClassicalRegister(8, "a"), 3)
551
+ Index(Var(ClassicalRegister(8, "a"), Uint(8)), Value(3, Uint(2)), Bool())
552
+ """
553
+ target, index = lift(target), lift(index)
554
+ if target.type.kind is not types.Uint or index.type.kind is not types.Uint:
555
+ raise TypeError(f"invalid types for indexing: '{target.type}' and '{index.type}'")
556
+ return Index(target, index, types.Bool())
@@ -31,6 +31,7 @@ __all__ = [
31
31
  import abc
32
32
  import enum
33
33
  import typing
34
+ import uuid
34
35
 
35
36
  from .. import types
36
37
 
@@ -110,29 +111,49 @@ class Cast(Expr):
110
111
  class Var(Expr):
111
112
  """A classical variable.
112
113
 
114
+ These variables take two forms: a new-style variable that owns its storage location and has an
115
+ associated name; and an old-style variable that wraps a :class:`.Clbit` or
116
+ :class:`.ClassicalRegister` instance that is owned by some containing circuit. In general,
117
+ construction of variables for use in programs should use :meth:`Var.new` or
118
+ :meth:`.QuantumCircuit.add_var`.
119
+
113
120
  Variables are immutable after construction, so they can be used as dictionary keys."""
114
121
 
115
- __slots__ = ("var",)
122
+ __slots__ = ("var", "name")
116
123
 
117
- var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister
124
+ var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID
118
125
  """A reference to the backing data storage of the :class:`Var` instance. When lifting
119
126
  old-style :class:`.Clbit` or :class:`.ClassicalRegister` instances into a :class:`Var`,
120
- this is exactly the :class:`.Clbit` or :class:`.ClassicalRegister`."""
127
+ this is exactly the :class:`.Clbit` or :class:`.ClassicalRegister`. If the variable is a
128
+ new-style classical variable (one that owns its own storage separate to the old
129
+ :class:`.Clbit`/:class:`.ClassicalRegister` model), this field will be a :class:`~uuid.UUID`
130
+ to uniquely identify it."""
131
+ name: str | None
132
+ """The name of the variable. This is required to exist if the backing :attr:`var` attribute
133
+ is a :class:`~uuid.UUID`, i.e. if it is a new-style variable, and must be ``None`` if it is
134
+ an old-style variable."""
121
135
 
122
136
  def __init__(
123
137
  self,
124
- var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister,
138
+ var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID,
125
139
  type: types.Type,
140
+ *,
141
+ name: str | None = None,
126
142
  ):
127
143
  super().__setattr__("type", type)
128
144
  super().__setattr__("var", var)
145
+ super().__setattr__("name", name)
146
+
147
+ @classmethod
148
+ def new(cls, name: str, type: types.Type) -> typing.Self:
149
+ """Generate a new named variable that owns its own backing storage."""
150
+ return cls(uuid.uuid4(), type, name=name)
129
151
 
130
152
  @property
131
153
  def standalone(self) -> bool:
132
- """Whether this :class:`Var` is a standalone variable that owns its storage location.
133
- This is currently always ``False``, but will expand in the future to support memory-owning
134
- storage locations."""
135
- return False
154
+ """Whether this :class:`Var` is a standalone variable that owns its storage location. If
155
+ false, this is a wrapper :class:`Var` around a pre-existing circuit object."""
156
+ return isinstance(self.var, uuid.UUID)
136
157
 
137
158
  def accept(self, visitor, /):
138
159
  return visitor.visit_var(self)
@@ -143,21 +164,29 @@ class Var(Expr):
143
164
  raise AttributeError(f"'Var' object has no attribute '{key}'")
144
165
 
145
166
  def __hash__(self):
146
- return hash((self.type, self.var))
167
+ return hash((self.type, self.var, self.name))
147
168
 
148
169
  def __eq__(self, other):
149
- return isinstance(other, Var) and self.type == other.type and self.var == other.var
170
+ return (
171
+ isinstance(other, Var)
172
+ and self.type == other.type
173
+ and self.var == other.var
174
+ and self.name == other.name
175
+ )
150
176
 
151
177
  def __repr__(self):
152
- return f"Var({self.var}, {self.type})"
178
+ if self.name is None:
179
+ return f"Var({self.var}, {self.type})"
180
+ return f"Var({self.var}, {self.type}, name='{self.name}')"
153
181
 
154
182
  def __getstate__(self):
155
- return (self.var, self.type)
183
+ return (self.var, self.type, self.name)
156
184
 
157
185
  def __setstate__(self, state):
158
- var, type = state
186
+ var, type, name = state
159
187
  super().__setattr__("type", type)
160
188
  super().__setattr__("var", var)
189
+ super().__setattr__("name", name)
161
190
 
162
191
  def __copy__(self):
163
192
  # I am immutable...
@@ -271,6 +300,11 @@ class Binary(Expr):
271
300
  The binary mathematical relations :data:`EQUAL`, :data:`NOT_EQUAL`, :data:`LESS`,
272
301
  :data:`LESS_EQUAL`, :data:`GREATER` and :data:`GREATER_EQUAL` take unsigned integers
273
302
  (with an implicit cast to make them the same width), and return a Boolean.
303
+
304
+ The bitshift operations :data:`SHIFT_LEFT` and :data:`SHIFT_RIGHT` can take bit-like
305
+ container types (e.g. unsigned integers) as the left operand, and any integer type as the
306
+ right-hand operand. In all cases, the output bit width is the same as the input, and zeros
307
+ fill in the "exposed" spaces.
274
308
  """
275
309
 
276
310
  # If adding opcodes, remember to add helper constructor functions in `constructors.py`
@@ -298,6 +332,10 @@ class Binary(Expr):
298
332
  """Numeric greater than. ``lhs > rhs``."""
299
333
  GREATER_EQUAL = 11
300
334
  """Numeric greater than or equal to. ``lhs >= rhs``."""
335
+ SHIFT_LEFT = 12
336
+ """Zero-padding bitshift to the left. ``lhs << rhs``."""
337
+ SHIFT_RIGHT = 13
338
+ """Zero-padding bitshift to the right. ``lhs >> rhs``."""
301
339
 
302
340
  def __str__(self):
303
341
  return f"Binary.{super().__str__()}"
@@ -325,3 +363,35 @@ class Binary(Expr):
325
363
 
326
364
  def __repr__(self):
327
365
  return f"Binary({self.op}, {self.left}, {self.right}, {self.type})"
366
+
367
+
368
+ @typing.final
369
+ class Index(Expr):
370
+ """An indexing expression.
371
+
372
+ Args:
373
+ target: The object being indexed.
374
+ index: The expression doing the indexing.
375
+ type: The resolved type of the result.
376
+ """
377
+
378
+ __slots__ = ("target", "index")
379
+
380
+ def __init__(self, target: Expr, index: Expr, type: types.Type):
381
+ self.target = target
382
+ self.index = index
383
+ self.type = type
384
+
385
+ def accept(self, visitor, /):
386
+ return visitor.visit_index(self)
387
+
388
+ def __eq__(self, other):
389
+ return (
390
+ isinstance(other, Index)
391
+ and self.type == other.type
392
+ and self.target == other.target
393
+ and self.index == other.index
394
+ )
395
+
396
+ def __repr__(self):
397
+ return f"Index({self.target}, {self.index}, {self.type})"
@@ -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)