qiskit 2.0.3__cp39-abi3-macosx_11_0_arm64.whl → 2.1.0__cp39-abi3-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +19 -1
  3. qiskit/_accelerate.abi3.so +0 -0
  4. qiskit/circuit/__init__.py +104 -20
  5. qiskit/circuit/_add_control.py +57 -31
  6. qiskit/circuit/_classical_resource_map.py +4 -0
  7. qiskit/circuit/annotation.py +504 -0
  8. qiskit/circuit/classical/expr/__init__.py +1 -1
  9. qiskit/circuit/classical/expr/expr.py +104 -446
  10. qiskit/circuit/classical/expr/visitors.py +6 -0
  11. qiskit/circuit/classical/types/types.py +7 -130
  12. qiskit/circuit/controlflow/box.py +32 -7
  13. qiskit/circuit/delay.py +11 -9
  14. qiskit/circuit/library/arithmetic/adders/adder.py +4 -4
  15. qiskit/circuit/library/arithmetic/multipliers/multiplier.py +2 -2
  16. qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +8 -4
  17. qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py +23 -15
  18. qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py +22 -14
  19. qiskit/circuit/library/arithmetic/quadratic_form.py +6 -0
  20. qiskit/circuit/library/arithmetic/weighted_adder.py +43 -24
  21. qiskit/circuit/library/basis_change/qft.py +2 -2
  22. qiskit/circuit/library/blueprintcircuit.py +6 -0
  23. qiskit/circuit/library/boolean_logic/inner_product.py +2 -2
  24. qiskit/circuit/library/boolean_logic/quantum_and.py +2 -2
  25. qiskit/circuit/library/boolean_logic/quantum_or.py +3 -3
  26. qiskit/circuit/library/boolean_logic/quantum_xor.py +2 -2
  27. qiskit/circuit/library/data_preparation/_z_feature_map.py +2 -2
  28. qiskit/circuit/library/data_preparation/_zz_feature_map.py +2 -2
  29. qiskit/circuit/library/data_preparation/pauli_feature_map.py +2 -2
  30. qiskit/circuit/library/fourier_checking.py +2 -2
  31. qiskit/circuit/library/generalized_gates/diagonal.py +5 -1
  32. qiskit/circuit/library/generalized_gates/gms.py +5 -1
  33. qiskit/circuit/library/generalized_gates/linear_function.py +2 -2
  34. qiskit/circuit/library/generalized_gates/permutation.py +5 -1
  35. qiskit/circuit/library/generalized_gates/uc.py +1 -1
  36. qiskit/circuit/library/generalized_gates/unitary.py +21 -2
  37. qiskit/circuit/library/graph_state.py +2 -2
  38. qiskit/circuit/library/grover_operator.py +2 -2
  39. qiskit/circuit/library/hidden_linear_function.py +2 -2
  40. qiskit/circuit/library/iqp.py +2 -2
  41. qiskit/circuit/library/n_local/efficient_su2.py +2 -2
  42. qiskit/circuit/library/n_local/evolved_operator_ansatz.py +1 -1
  43. qiskit/circuit/library/n_local/excitation_preserving.py +7 -9
  44. qiskit/circuit/library/n_local/n_local.py +4 -3
  45. qiskit/circuit/library/n_local/pauli_two_design.py +2 -2
  46. qiskit/circuit/library/n_local/real_amplitudes.py +2 -2
  47. qiskit/circuit/library/n_local/two_local.py +2 -2
  48. qiskit/circuit/library/overlap.py +2 -2
  49. qiskit/circuit/library/pauli_evolution.py +3 -2
  50. qiskit/circuit/library/phase_estimation.py +2 -2
  51. qiskit/circuit/library/standard_gates/dcx.py +11 -12
  52. qiskit/circuit/library/standard_gates/ecr.py +21 -24
  53. qiskit/circuit/library/standard_gates/equivalence_library.py +232 -96
  54. qiskit/circuit/library/standard_gates/global_phase.py +5 -6
  55. qiskit/circuit/library/standard_gates/h.py +22 -45
  56. qiskit/circuit/library/standard_gates/i.py +1 -1
  57. qiskit/circuit/library/standard_gates/iswap.py +13 -31
  58. qiskit/circuit/library/standard_gates/p.py +19 -26
  59. qiskit/circuit/library/standard_gates/r.py +11 -17
  60. qiskit/circuit/library/standard_gates/rx.py +21 -45
  61. qiskit/circuit/library/standard_gates/rxx.py +7 -22
  62. qiskit/circuit/library/standard_gates/ry.py +21 -39
  63. qiskit/circuit/library/standard_gates/ryy.py +13 -28
  64. qiskit/circuit/library/standard_gates/rz.py +18 -35
  65. qiskit/circuit/library/standard_gates/rzx.py +7 -22
  66. qiskit/circuit/library/standard_gates/rzz.py +7 -19
  67. qiskit/circuit/library/standard_gates/s.py +44 -39
  68. qiskit/circuit/library/standard_gates/swap.py +25 -38
  69. qiskit/circuit/library/standard_gates/sx.py +34 -41
  70. qiskit/circuit/library/standard_gates/t.py +18 -27
  71. qiskit/circuit/library/standard_gates/u.py +8 -24
  72. qiskit/circuit/library/standard_gates/u1.py +28 -52
  73. qiskit/circuit/library/standard_gates/u2.py +9 -9
  74. qiskit/circuit/library/standard_gates/u3.py +24 -40
  75. qiskit/circuit/library/standard_gates/x.py +190 -336
  76. qiskit/circuit/library/standard_gates/xx_minus_yy.py +12 -50
  77. qiskit/circuit/library/standard_gates/xx_plus_yy.py +13 -52
  78. qiskit/circuit/library/standard_gates/y.py +19 -23
  79. qiskit/circuit/library/standard_gates/z.py +31 -38
  80. qiskit/circuit/parameter.py +14 -5
  81. qiskit/circuit/parameterexpression.py +109 -75
  82. qiskit/circuit/quantumcircuit.py +172 -99
  83. qiskit/circuit/quantumcircuitdata.py +1 -0
  84. qiskit/circuit/random/__init__.py +37 -2
  85. qiskit/circuit/random/utils.py +445 -56
  86. qiskit/circuit/tools/pi_check.py +5 -13
  87. qiskit/compiler/transpiler.py +1 -1
  88. qiskit/converters/circuit_to_instruction.py +2 -2
  89. qiskit/dagcircuit/dagnode.py +8 -3
  90. qiskit/primitives/__init__.py +2 -2
  91. qiskit/primitives/base/base_estimator.py +2 -2
  92. qiskit/primitives/containers/data_bin.py +0 -3
  93. qiskit/primitives/containers/observables_array.py +192 -108
  94. qiskit/primitives/primitive_job.py +29 -10
  95. qiskit/providers/fake_provider/generic_backend_v2.py +2 -0
  96. qiskit/qasm3/__init__.py +106 -12
  97. qiskit/qasm3/ast.py +15 -1
  98. qiskit/qasm3/exporter.py +59 -36
  99. qiskit/qasm3/printer.py +12 -0
  100. qiskit/qpy/__init__.py +182 -6
  101. qiskit/qpy/binary_io/circuits.py +256 -24
  102. qiskit/qpy/binary_io/parse_sympy_repr.py +5 -0
  103. qiskit/qpy/binary_io/schedules.py +12 -32
  104. qiskit/qpy/binary_io/value.py +36 -18
  105. qiskit/qpy/common.py +11 -3
  106. qiskit/qpy/formats.py +17 -1
  107. qiskit/qpy/interface.py +52 -12
  108. qiskit/qpy/type_keys.py +7 -1
  109. qiskit/quantum_info/__init__.py +10 -0
  110. qiskit/quantum_info/operators/__init__.py +1 -0
  111. qiskit/quantum_info/operators/symplectic/__init__.py +1 -0
  112. qiskit/quantum_info/operators/symplectic/clifford_circuits.py +26 -0
  113. qiskit/quantum_info/operators/symplectic/pauli.py +2 -2
  114. qiskit/result/sampled_expval.py +3 -1
  115. qiskit/synthesis/__init__.py +10 -0
  116. qiskit/synthesis/arithmetic/__init__.py +1 -1
  117. qiskit/synthesis/arithmetic/adders/__init__.py +1 -0
  118. qiskit/synthesis/arithmetic/adders/draper_qft_adder.py +6 -2
  119. qiskit/synthesis/arithmetic/adders/rv_ripple_carry_adder.py +156 -0
  120. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +14 -126
  121. qiskit/synthesis/discrete_basis/solovay_kitaev.py +161 -121
  122. qiskit/synthesis/evolution/lie_trotter.py +10 -7
  123. qiskit/synthesis/evolution/product_formula.py +10 -7
  124. qiskit/synthesis/evolution/qdrift.py +10 -7
  125. qiskit/synthesis/evolution/suzuki_trotter.py +10 -7
  126. qiskit/synthesis/multi_controlled/__init__.py +4 -0
  127. qiskit/synthesis/multi_controlled/mcx_synthesis.py +402 -178
  128. qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py +14 -15
  129. qiskit/synthesis/qft/qft_decompose_lnn.py +7 -25
  130. qiskit/synthesis/unitary/qsd.py +80 -9
  131. qiskit/transpiler/__init__.py +10 -3
  132. qiskit/transpiler/instruction_durations.py +2 -20
  133. qiskit/transpiler/passes/__init__.py +5 -2
  134. qiskit/transpiler/passes/layout/dense_layout.py +26 -6
  135. qiskit/transpiler/passes/layout/disjoint_utils.py +1 -166
  136. qiskit/transpiler/passes/layout/sabre_layout.py +22 -3
  137. qiskit/transpiler/passes/layout/sabre_pre_layout.py +1 -1
  138. qiskit/transpiler/passes/layout/vf2_layout.py +49 -13
  139. qiskit/transpiler/passes/layout/vf2_utils.py +10 -0
  140. qiskit/transpiler/passes/optimization/__init__.py +1 -1
  141. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +2 -1
  142. qiskit/transpiler/passes/optimization/optimize_clifford_t.py +68 -0
  143. qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +3 -9
  144. qiskit/transpiler/passes/routing/sabre_swap.py +4 -2
  145. qiskit/transpiler/passes/routing/star_prerouting.py +106 -81
  146. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  147. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
  148. qiskit/transpiler/passes/scheduling/padding/__init__.py +1 -0
  149. qiskit/transpiler/passes/scheduling/padding/context_aware_dynamical_decoupling.py +876 -0
  150. qiskit/transpiler/passes/synthesis/__init__.py +1 -0
  151. qiskit/transpiler/passes/synthesis/clifford_unitary_synth_plugin.py +123 -0
  152. qiskit/transpiler/passes/synthesis/hls_plugins.py +494 -93
  153. qiskit/transpiler/passes/synthesis/plugin.py +4 -0
  154. qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +27 -22
  155. qiskit/transpiler/passmanager_config.py +3 -0
  156. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +149 -28
  157. qiskit/transpiler/preset_passmanagers/common.py +101 -0
  158. qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +6 -0
  159. qiskit/transpiler/preset_passmanagers/level3.py +2 -2
  160. qiskit/transpiler/target.py +15 -2
  161. qiskit/utils/optionals.py +6 -5
  162. qiskit/visualization/circuit/_utils.py +5 -3
  163. qiskit/visualization/circuit/latex.py +9 -2
  164. qiskit/visualization/circuit/matplotlib.py +26 -4
  165. qiskit/visualization/circuit/qcstyle.py +9 -157
  166. qiskit/visualization/dag/__init__.py +13 -0
  167. qiskit/visualization/dag/dagstyle.py +103 -0
  168. qiskit/visualization/dag/styles/__init__.py +13 -0
  169. qiskit/visualization/dag/styles/color.json +10 -0
  170. qiskit/visualization/dag/styles/plain.json +5 -0
  171. qiskit/visualization/dag_visualization.py +169 -98
  172. qiskit/visualization/style.py +223 -0
  173. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/METADATA +7 -6
  174. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/RECORD +178 -169
  175. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/entry_points.txt +6 -0
  176. qiskit/synthesis/discrete_basis/commutator_decompose.py +0 -265
  177. qiskit/synthesis/discrete_basis/gate_sequence.py +0 -421
  178. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/WHEEL +0 -0
  179. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/licenses/LICENSE.txt +0 -0
  180. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,504 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2025.
4
+ #
5
+ # This code is licensed under the Apache License, Version 2.0. You may
6
+ # obtain a copy of this license in the LICENSE.txt file in the root directory
7
+ # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
+ #
9
+ # Any modifications or derivative works of this code must retain this
10
+ # copyright notice, and modified files need to carry a notice indicating
11
+ # that they have been altered from the originals.
12
+
13
+ """
14
+ ======================================================
15
+ Circuit annotations (:mod:`qiskit.circuit.annotation`)
16
+ ======================================================
17
+
18
+ .. currentmodule:: qiskit.circuit.annotation
19
+
20
+ This module contains the infrastructure for working with custom circuit annotations.
21
+
22
+ The main user-facing class is the base class :class:`qiskit.circuit.Annotation`, which is also
23
+ re-exported from this module.
24
+
25
+ .. _circuit-annotation-subclassing:
26
+
27
+ Custom annotation subclasses
28
+ ============================
29
+
30
+ The :class:`.Annotation` class is intended to be subclassed. Subclasses must set their
31
+ :attr:`~.Annotation.namespace` field. This can be specific to an instance, or static for an entire
32
+ subclass. The namespace is used as part of the dispatch mechanism, as described in
33
+ :ref:`circuit-annotation-namespacing`.
34
+
35
+ Circuit equality checks also compare annotations on objects in an order-dependent manner. You will
36
+ likely want to implement the :meth:`~object.__eq__` magic method on any subclasses.
37
+
38
+ If you intend your annotation to be able to be serialized via :ref:`QPY <qiskit-qpy>` or :ref:`
39
+ OpenQASM 3 <qiskit-qasm3>`, you must provide separate implementations of the serialization and
40
+ deserialization methods as discussed in :ref:`circuit-annotation-serialization`.
41
+
42
+ .. _circuit-annotation-namespacing:
43
+
44
+ Namespacing
45
+ -----------
46
+
47
+ The "namespace" of an annotation is used as a look-up key when any consumer is deciding which
48
+ handler to invoke. This includes in QPY and OpenQASM 3 serialization contexts, but in general,
49
+ transpiler passes will also look at annotations' namespaces to determine if they are relevant, and
50
+ so on.
51
+
52
+ This can be standard Python identifier (e.g. ``my_namespace``), or a dot-separated list of
53
+ identifiers (e.g. ``my_namespace.subnamespace``). The namespace is used by all consumers of
54
+ annotations to determine what handler should be invoked.
55
+
56
+ A stand-alone function allows iterating through namespaces and parent namespaces in priority order
57
+ from most specific to least specific.
58
+
59
+ .. autofunction:: iter_namespaces
60
+
61
+
62
+ .. _circuit-annotation-serialization:
63
+
64
+ Serialization and deserialization
65
+ ---------------------------------
66
+
67
+ Annotations represent completely custom data, that may persist after compilation. This may include
68
+ data that should be serialized for later consumption, such as additional data that is interpreted by
69
+ a backend-compiler. Qiskit's native binary QPY format (see :mod:`qiskit.qpy`) supports the concept
70
+ of arbitrary annotations in its payloads from version 15 onwards. In OpenQASM 3 (see
71
+ :mod:`qiskit.qasm3`), annotations are a core language feature, and Qiskit's import/export support
72
+ for OpenQASM 3 includes serialization of annotations.
73
+
74
+ However, since annotations are generally custom subclasses and unknown to Qiskit, we cannot have
75
+ built-in support for serialization. On the deserialization front, Qiskit will not, in general, have
76
+ an existing :class:`~.Annotation` object to call deserialization methods from. It is also expected
77
+ that annotations may relate to some unknown-to-Qiskit shared state within a given circuit context.
78
+
79
+ For all of these reasons, serialization and deserialization of annotations is handled by custom
80
+ objects, which must be passed at the interface points of the relevant serialization functions. For
81
+ example in QPY, the ``annotation_factories`` argument in :func:`.qpy.dump` and :func:`.qpy.load` are
82
+ used to pass serializers.
83
+
84
+ .. autoclass:: QPYSerializer
85
+ .. autoclass:: QPYFromOpenQASM3Serializer
86
+ .. autoclass:: OpenQASM3Serializer
87
+
88
+
89
+ Examples
90
+ ========
91
+
92
+ A block-collection transpiler pass
93
+ ----------------------------------
94
+
95
+ A principal goal of the annotation framework is to allow custom analyses and commands to be stored
96
+ on circuits in an instruction-local manner, either by the user on entry to the compiler, or for one
97
+ compiler pass to store information for later consumption.
98
+
99
+ For example, we can write a simple transpiler pass that collects runs of single-qubit operations,
100
+ and puts each run into a :class:`.BoxOp`, the calculates the total unitary action and attaches it as
101
+ a custom annotation, so the same analysis does not need to be repeated later, even if the internals
102
+ of each block are optimized.
103
+
104
+ .. code-block:: python
105
+
106
+ from qiskit.circuit import annotation, QuantumCircuit, BoxOp
107
+ from qiskit.quantum_info import Operator
108
+ from qiskit.transpiler import TransformationPass
109
+
110
+ class PerformsUnitary(annotation.Annotation):
111
+ namespace = "unitary"
112
+ def __init__(self, matrix):
113
+ self.matrix = matrix
114
+
115
+ class Collect1qRuns(TransformationPass):
116
+ def run(self, dag):
117
+ for run in dag.collect_1q_runs():
118
+ block = QuantumCircuit(1)
119
+ for node in run:
120
+ block.append(node.op, [0], [])
121
+ box = BoxOp(block, annotations=[PerformsUnitary(Operator(block).data)])
122
+ dag.replace_block_with_op(run, box, {run[0].qargs[0]: 0})
123
+ return dag
124
+
125
+ In order to serialize the annotation to OpenQASM 3, we must define custom logic, since the analysis
126
+ itself is entirely custom. The serialization is separate to the annotation; there may be
127
+ circumstances in which serialization should be done differently.
128
+
129
+ .. code-block:: python
130
+
131
+ import ast
132
+ import numpy as np
133
+
134
+ class Serializer(annotation.OpenQASM3Serializer):
135
+ def dump(self, annotation):
136
+ if annotation.namespace != "unitary":
137
+ return NotImplemented
138
+ line = lambda row: "[" + ", ".join(repr(x) for x in row) + "]"
139
+ return "[" + ", ".join(line(row) for row in annotation.matrix.tolist()) + "]"
140
+
141
+ def load(self, namespace, payload):
142
+ if namespace != "unitary":
143
+ return NotImplemented
144
+ return PerformsUnitary(np.array(ast.literal_eval(payload), dtype=complex))
145
+
146
+ Finally, this can be put together, showing the output OpenQASM 3.
147
+
148
+ .. code-block:: python
149
+
150
+ from qiskit import qasm3
151
+
152
+ qc = QuantumCircuit(3)
153
+ qc.s(0)
154
+ qc.t(0)
155
+ qc.y(1)
156
+ qc.x(1)
157
+ qc.h(2)
158
+ qc.s(2)
159
+ collected = Collect1qRuns()(qc)
160
+
161
+ handlers = {"unitary": Serializer()}
162
+ dumped = qasm3.dumps(collected, annotation_handlers=handlers)
163
+ print(dumped)
164
+
165
+ .. code-block:: openqasm3
166
+
167
+ OPENQASM 3.0;
168
+ include "stdgates.inc";
169
+ qubit[3] q;
170
+ @unitary [[(1+0j), 0j], [0j, (-0.7071067811865475+0.7071067811865475j)]]
171
+ box {
172
+ s q[0];
173
+ t q[0];
174
+ }
175
+ @unitary [[1j, 0j], [0j, -1j]]
176
+ box {
177
+ y q[1];
178
+ x q[1];
179
+ }
180
+ @unitary [[(0.7071067811865475+0j), (0.7071067811865475+0j)], [0.7071067811865475j, \
181
+ -0.7071067811865475j]]
182
+ box {
183
+ h q[2];
184
+ s q[2];
185
+ }
186
+
187
+ """
188
+
189
+ from __future__ import annotations
190
+
191
+ import abc
192
+ from typing import Literal, Iterator
193
+
194
+ from qiskit._accelerate.circuit import Annotation
195
+
196
+
197
+ __all__ = [
198
+ "Annotation", # Also exported in `qiskit.circuit`, but for convenience is here too.
199
+ "QPYSerializer",
200
+ "OpenQASM3Serializer",
201
+ "QPYFromOpenQASM3Serializer",
202
+ "iter_namespaces",
203
+ ]
204
+
205
+
206
+ def iter_namespaces(namespace: str) -> Iterator[str]:
207
+ """An iterator over all namespaces that can be used to lookup the given namespace.
208
+
209
+ This includes the namespace and all parents, including the root empty-string namespace.
210
+
211
+ Examples:
212
+
213
+ .. code-block:: python
214
+
215
+ from qiskit.circuit.annotation import iter_namespaces
216
+ assert list(iter_namespaces("hello.world")) == ["hello.world", "hello", ""]
217
+ """
218
+ while namespace:
219
+ yield namespace
220
+ split = namespace.rsplit(".", 1)
221
+ if len(split) == 1:
222
+ break
223
+ namespace = split[0]
224
+ yield ""
225
+
226
+
227
+ class QPYSerializer(abc.ABC):
228
+ """The interface for serializers and deserializers of :class:`.Annotation` objects to QPY.
229
+
230
+ For more information on QPY, see :mod:`qiskit.qpy`.
231
+
232
+ This interface-definition class is designed to be subclassed. The individual methods describe
233
+ their contracts, and how they will be called.
234
+
235
+ During QPY serialization and deserialization, the main QPY logic will call a factory function to
236
+ create instances of subclasses of this class. The return value from a given factory function
237
+ will be used in *either* a serialization or deserialization context, but not both.
238
+
239
+ The structure of calls during serialization of a single circuit is:
240
+
241
+ 1. many calls to :meth:`dump_annotation`, which will all share the same ``namespace`` argument,
242
+ which will always be a (non-strict) prefix of all the :class:`.Annotation` objects given.
243
+ 2. one call to :meth:`dump_state`.
244
+
245
+ The general structure of calls during deserialization of a single circuit out of a QPY payload
246
+ is:
247
+
248
+ 1. one call to :meth:`load_state`, passing a ``namespace`` (with the same non-strict prefixing
249
+ behavior as the "serializing" form).
250
+ 2. many calls to :meth:`load_annotation`, corresponding to annotations serialized under that
251
+ namespace-prefix lookup.
252
+
253
+ When subclassing this, recall that QPY is intended to have strict backwards-compatibility
254
+ guarantees, and it is strongly recommended that annotation-serialisation subclasses maintain
255
+ this. In particular, it is suggested that any non-trivial serializer includes "version"
256
+ information for the serializer in its total "state" (see :meth:`dump_state`), and the
257
+ deserialization should make every effort to support backwards compatibility with previous
258
+ versions of the same serializer.
259
+ """
260
+
261
+ @abc.abstractmethod
262
+ def dump_annotation(
263
+ self, namespace: str, annotation: Annotation
264
+ ) -> bytes | Literal[NotImplemented]:
265
+ """Serialize an annotation to a bytestream.
266
+
267
+ This method may mutate the serializer's internal state (the object that will be serialized
268
+ by :meth:`dump_state`).
269
+
270
+ The ``namespace`` argument is the resolved key used to lookup this serializer. It may not
271
+ be identical to the :attr:`.Annotation.namespace` field of the ``annotation`` argument; it
272
+ might be an ancestor, up to and including the empty string (the root namespace). All calls
273
+ to an instance of this class, as retrieved by a factory function in :func:`.qpy.dump` will
274
+ be made using the same ``namespace``.
275
+
276
+ The method can return :data:`NotImplemented` if the serializer cannot serialize a particular
277
+ annotation. In this case, the QPY logic will attempt to use a serializer registered for
278
+ the parent namespaces.
279
+
280
+ This method is the mirror of :meth:`load_annotation`.
281
+
282
+ Args:
283
+ namespace: the namespace that this serializer was accessed under. This may be an
284
+ ancestor of the annotation.
285
+ annotation: the object to serialize to a bytestream.
286
+
287
+ Returns:
288
+ Either the serialized form of the annotation (optionally after mutating the
289
+ serialization state of this class), or :data:`NotImplemented` if the annotation cannot
290
+ be handled.
291
+ """
292
+
293
+ @abc.abstractmethod
294
+ def load_annotation(self, payload: bytes) -> Annotation:
295
+ """Load an annotation from a view of memory.
296
+
297
+ A subclass can assume that :meth:`load_state` will have been called exactly once before
298
+ this method is called, and all subsequent calls to this method will be for payloads
299
+ corresponding to annotations serialized under that parent namespace.
300
+
301
+ If a user configures QPY correctly, instances of this class will only be asked to
302
+ deserialize payloads that the corresponding :meth:`dump_annotation` can successfully handle
303
+ (i.e. return a payload, not :data:`NotImplemented`). Subclasses may raise an arbitrary
304
+ exception if this is not the case and this will abort the QPY load operation. Such a
305
+ situation would require that the user supplied a different serializer configuration on the
306
+ two sides of the QPY load and dump.
307
+
308
+ This method is the mirror of :meth:`dump_annotation`.
309
+
310
+ Args:
311
+ payload: the bytes to deserialized into an annotation.
312
+
313
+ Returns:
314
+ The deserialized annotation.
315
+ """
316
+
317
+ def dump_state(self) -> bytes:
318
+ """Serialize a state object for the given serializer.
319
+
320
+ When in a QPY dumping context, this method will be called exactly once, after all calls to
321
+ :meth:`dump_annotation`.
322
+
323
+ The default state is the empty bytestring; if your serializer is stateless, you do not need
324
+ to override this method.
325
+
326
+ This method is the mirror of :meth:`load_state`.
327
+ """
328
+ return b""
329
+
330
+ def load_state(self, namespace: str, payload: bytes): # pylint: disable=unused-argument
331
+ """Initialize the state of the deserializer for a given ``namespace`` key.
332
+
333
+ When in a QPY loading context, this method will be called exactly once, before all calls to
334
+ :meth:`load_annotation`. The ``namespace`` will be the same namespace that was passed to
335
+ all calls to :meth:`dump_annotation` in the dumping context; that is, a (non-strict) prefix
336
+ of the namespaces of all the :class:`.Annotation` objects its counterpart was asked to
337
+ serialize. For example, if the QPY dump was configured with::
338
+
339
+ from qiskit import qpy
340
+ from qiskit.circuit import annotation, Annotation
341
+
342
+ class MyA(Annotation):
343
+ namespace = "my.a"
344
+ class MyB(Annotation):
345
+ namespace = "my.b"
346
+
347
+ class MyQPYSerializer(annotation.QPYSerializer):
348
+ ...
349
+
350
+ qpy.dump(..., annotation_factories={"my": MyQPYSerializer})
351
+
352
+ then during the corresponding call to :func:`.qpy.load`, this method in ``MyQPYSerializer``
353
+ will be called with ``"my"``, even though the annotations serialized had namespaces ``my.a``
354
+ and ``my.b``. It is up to individual dumpers to do any sub-namespace handling they choose.
355
+
356
+ The default implementation is a no-op; if you have not overridden :meth:`dump_state`, you do
357
+ not need to override this method.
358
+
359
+ This method is the mirror of :meth:`dump_state`.
360
+
361
+ Args:
362
+ namespace: the namespace key that the corresponding dump was resolved under.
363
+ payload: the state payload that was dumped by the corresponding call to
364
+ :meth:`dump_state`.
365
+ """
366
+ pass
367
+
368
+
369
+ class OpenQASM3Serializer(abc.ABC):
370
+ """The interface for serializers and deserializers of :class:`.Annotation` objects to
371
+ OpenQASM 3.
372
+
373
+ For more information on OpenQASM 3 support in Qiskit, see :mod:`qiskit.qasm3`.
374
+
375
+ This interface-definition class is designed to be subclassed. OpenQASM 3 annotations are
376
+ stateless within a program, therefore a subclass must not track state.
377
+ """
378
+
379
+ @abc.abstractmethod
380
+ def dump(self, annotation: Annotation) -> str | Literal[NotImplemented]:
381
+ """Serialize the paylaod of an annotation to a single line of UTF-8 text.
382
+
383
+ The output of this method should not include the annotation's
384
+ :attr:`~.Annotation.namespace` attribute; this is handled automatically by the OpenQASM 3
385
+ exporter.
386
+
387
+ The serialized form must not contain newline characters; it must be valid as the "arbitrary"
388
+ component of the annotation as defined by OpenQASM 3. If there is no data required, the
389
+ method should return the empty string. If this serializer cannot handle the particular
390
+ annotation, it should return :data:`NotImplemented`.
391
+
392
+ Args:
393
+ annotation: the annotation object to serialize.
394
+
395
+ Returns:
396
+ the serialized annotation (without the namespace component), or the sentinel
397
+ :data:`NotImplemented` if it cannot be handled by this object.
398
+ """
399
+
400
+ @abc.abstractmethod
401
+ def load(self, namespace: str, payload: str) -> Annotation | Literal[NotImplemented]:
402
+ """Load an annotation, if possible, from an OpenQASM 3 program.
403
+
404
+ The two arguments will be the two components of an annotation, as defined by the OpenQASM 3
405
+ specification. The method should return :data:`NotImplemented` if it cannot handle the
406
+ annotation.
407
+
408
+ Args:
409
+ namespace: the OpenQASM 3 "namespace" of the annotation.
410
+ payload: the rest of the payload for the annotation. This is arbitrary and free-form,
411
+ and in general should have been serialized by a call to :meth:`dump`.
412
+
413
+ Returns:
414
+ the created :class:`.Annotation` object, whose :attr:`.Annotation.namespace` attribute
415
+ should be identical to the incoming ``namespace`` argument. If this class cannot handle
416
+ the annotation, it can also return :data:`NotImplemented`.
417
+ """
418
+
419
+ def as_qpy(self) -> QPYFromOpenQASM3Serializer:
420
+ """Derive a serializer/deserializer for QPY from this OpenQASM 3 variant.
421
+
422
+ OpenQASM 3 serialization and deserialization is intended to be stateless and return single
423
+ lines of UTF-8 encoded text. This is a subset of the allowable serializations for QPY."""
424
+ return QPYFromOpenQASM3Serializer(self)
425
+
426
+
427
+ class QPYFromOpenQASM3Serializer(QPYSerializer):
428
+ """An adaptor that converts a :class:`OpenQASM3Serializer` into a :class:`QPYSerializer`.
429
+
430
+ This works because OpenQASM 3 annotation serializers are required to be stateless and return
431
+ UTF-8-encoded single lines of text, which is a subset of what QPY permits.
432
+
433
+ Typically you create one of these using the :meth:`~OpenQASM3Serializer.as_qpy` method
434
+ of an OpenQASM 3 annotation serializer.
435
+
436
+ Examples:
437
+
438
+ Instances of this class can be called like a zero-argument function and return themselves. This
439
+ lets you use them directly as a factory function to the QPY entry points, such as:
440
+
441
+ .. code-block:: python
442
+
443
+ import io
444
+ from qiskit.circuit import OpenQASM3Serializer, Annotation
445
+ from qiskit import qpy
446
+
447
+ class MyAnnotation(Annotation):
448
+ namespace = "my_namespace"
449
+
450
+ class MySerializer(OpenQASM3Serializer):
451
+ def dump(self, annotation):
452
+ if not isinstance(annotation, MyAnnotation):
453
+ return NotImplemented
454
+ return ""
455
+
456
+ def load(self, namespace, payload):
457
+ assert namespace == "my_namespace"
458
+ assert payload == ""
459
+ return MyAnnotation()
460
+
461
+ qc = QuantumCircuit(2)
462
+ with qc.box(annotations=[MyAnnotation()]):
463
+ qc.cx(0, 1)
464
+
465
+ with io.BytesIO() as fptr:
466
+ qpy.dump(fptr, qc, annotation_serializers = {"my_namespace": MySerializer().as_qpy()})
467
+
468
+ This is safe, without returning separate instances, because the base OpenQASM 3 serializers are
469
+ necessarily stateless.
470
+ """
471
+
472
+ def __init__(self, inner: OpenQASM3Serializer):
473
+ """
474
+ Args:
475
+ inner: the OpenQASM 3 serializer that this is derived from.
476
+ """
477
+ self.inner = inner
478
+
479
+ def dump_annotation(self, namespace, annotation):
480
+ qasm3 = self.inner.dump(annotation)
481
+ if qasm3 is NotImplemented:
482
+ return NotImplemented
483
+ return (
484
+ # For OpenQASM 3 serialisation, we need the exact namespace the annotation claims.
485
+ annotation.namespace.encode("utf-8")
486
+ + b"\x00"
487
+ + qasm3.encode("utf-8")
488
+ )
489
+
490
+ def load_annotation(self, payload):
491
+ namespace, payload = payload.split(b"\x00", maxsplit=1)
492
+ out = self.inner.load(namespace.decode("utf-8"), payload.decode("utf-8"))
493
+ if out is NotImplemented:
494
+ raise ValueError(
495
+ "asked to deserialize an object the provided OpenQASM deserializer cannot handle"
496
+ )
497
+ return out
498
+
499
+ def __call__(self) -> QPYFromOpenQASM3Serializer:
500
+ # Our internal object is stateless because it's an OpenQASM 3 exporter (which is a stateless
501
+ # format). Defining this method allows an instance of ourself to be used as a factory
502
+ # function, simplifying the interface for creating a QPY serializer from an OpenQASM 3 one,
503
+ # and attaching this to the class means that pickling would "just work".
504
+ return self
@@ -39,7 +39,7 @@ 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
- All :class`Expr` instances define a boolean :attr:`~Expr.const` attribute, which indicates
42
+ All :class:`Expr` instances define a boolean :attr:`~Expr.const` attribute, which indicates
43
43
  whether the expression can be evaluated at compile time. Most expression classes infer this
44
44
  during construction based on the const-ness of their operands.
45
45