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
qiskit/qasm3/__init__.py CHANGED
@@ -226,7 +226,10 @@ available on this module, if you wish to build on top of it:
226
226
  ``stdgates.inc`` include file.
227
227
  """
228
228
 
229
+ from __future__ import annotations
230
+
229
231
  import functools
232
+ import typing
230
233
  import warnings
231
234
 
232
235
  from qiskit._accelerate import qasm3 as _qasm3
@@ -234,11 +237,13 @@ from qiskit.circuit import library
234
237
  from qiskit.exceptions import ExperimentalWarning
235
238
  from qiskit.utils import optionals as _optionals
236
239
 
240
+ from .._accelerate.qasm3 import CustomGate
241
+ from .exceptions import QASM3Error, QASM3ExporterError, QASM3ImporterError
237
242
  from .experimental import ExperimentalFeatures
238
243
  from .exporter import Exporter
239
- from .exceptions import QASM3Error, QASM3ImporterError, QASM3ExporterError
240
- from .._accelerate.qasm3 import CustomGate
241
244
 
245
+ if typing.TYPE_CHECKING:
246
+ from qiskit.circuit import annotation, QuantumCircuit
242
247
 
243
248
  STDGATES_INC_GATES = (
244
249
  CustomGate(library.PhaseGate, "p", 1, 1),
@@ -303,11 +308,15 @@ def dump(circuit, stream, **kwargs) -> None:
303
308
 
304
309
 
305
310
  @_optionals.HAS_QASM3_IMPORT.require_in_call("loading from OpenQASM 3")
306
- def load(filename: str):
311
+ def load(
312
+ filename: str, *, annotation_handlers: dict[str, annotation.OpenQASM3Serializer] | None = None
313
+ ) -> QuantumCircuit:
307
314
  """Load an OpenQASM 3 program from the file ``filename``.
308
315
 
309
316
  Args:
310
317
  filename: the filename to load the program from.
318
+ annotation_handlers: a mapping whose keys are (parent) namespaces and values are serializers
319
+ that can handle children of those namesapces. Requires ``qiskit_qasm3_import>=0.6.0``.
311
320
 
312
321
  Returns:
313
322
  QuantumCircuit: a circuit representation of the OpenQASM 3 program.
@@ -315,24 +324,25 @@ def load(filename: str):
315
324
  Raises:
316
325
  QASM3ImporterError: if the OpenQASM 3 file is invalid, or cannot be represented by a
317
326
  :class:`.QuantumCircuit`.
318
- """
319
-
320
- import qiskit_qasm3_import
321
327
 
328
+ .. versionadded:: 2.1
329
+ The ``annotation_handlers`` argument. This requires ``qiskit_qasm3_import>=0.6.0``.
330
+ """
322
331
  with open(filename, "r") as fptr:
323
332
  program = fptr.read()
324
- try:
325
- return qiskit_qasm3_import.parse(program)
326
- except qiskit_qasm3_import.ConversionError as exc:
327
- raise QASM3ImporterError(str(exc)) from exc
333
+ return loads(program, annotation_handlers=annotation_handlers)
328
334
 
329
335
 
330
336
  @_optionals.HAS_QASM3_IMPORT.require_in_call("loading from OpenQASM 3")
331
- def loads(program: str):
337
+ def loads(
338
+ program: str, *, annotation_handlers: dict[str, annotation.OpenQASM3Serializer] | None = None
339
+ ) -> QuantumCircuit:
332
340
  """Load an OpenQASM 3 program from the given string.
333
341
 
334
342
  Args:
335
343
  program: the OpenQASM 3 program.
344
+ annotation_handlers: a mapping whose keys are (parent) namespaces and values are serializers
345
+ that can handle children of those namesapces. Requires ``qiskit_qasm3_import>=0.6.0``.
336
346
 
337
347
  Returns:
338
348
  QuantumCircuit: a circuit representation of the OpenQASM 3 program.
@@ -340,12 +350,23 @@ def loads(program: str):
340
350
  Raises:
341
351
  QASM3ImporterError: if the OpenQASM 3 file is invalid, or cannot be represented by a
342
352
  :class:`.QuantumCircuit`.
353
+
354
+ .. versionadded:: 2.1
355
+ The ``annotation_handlers`` argument. This requires ``qiskit_qasm3_import>=0.6.0``.
343
356
  """
344
357
 
345
358
  import qiskit_qasm3_import
346
359
 
360
+ kwargs = {}
361
+ if annotation_handlers is not None:
362
+ if getattr(qiskit_qasm3_import, "VERSION_PARTS", (0, 0)) < (0, 6):
363
+ raise QASM3ImporterError(
364
+ "need 'qiskit_qasm3_import>=0.6.0' to handle annotations, but you have "
365
+ + qiskit_qasm3_import.__version__
366
+ )
367
+ kwargs["annotation_handlers"] = annotation_handlers
347
368
  try:
348
- return qiskit_qasm3_import.parse(program)
369
+ return qiskit_qasm3_import.parse(program, **kwargs)
349
370
  except qiskit_qasm3_import.ConversionError as exc:
350
371
  raise QASM3ImporterError(str(exc)) from exc
351
372
 
@@ -370,3 +391,76 @@ def load_experimental(pathlike_or_filelike, /, *, custom_gates=None, include_pat
370
391
  category=ExperimentalWarning,
371
392
  )
372
393
  return _qasm3.load(pathlike_or_filelike, custom_gates=custom_gates, include_path=include_path)
394
+
395
+
396
+ @functools.wraps(_qasm3.dumps)
397
+ def dumps_experimental(
398
+ circuit,
399
+ *,
400
+ includes=None,
401
+ basis_gates=None,
402
+ disable_constants=True,
403
+ alias_classical_registers=False,
404
+ allow_aliasing=False,
405
+ indent=" ",
406
+ ):
407
+ """<overridden by functools.wraps>"""
408
+ warnings.warn(
409
+ "This is an experimental version of serialization of a :class:`~qiskit.circuit.QuantumCircuit`"
410
+ " object in an OpenQASM 3 string."
411
+ " Beware that its interface might change, and it might be missing features.",
412
+ category=ExperimentalWarning,
413
+ )
414
+ if includes is None:
415
+ includes = ["stdgates.inc"]
416
+ if basis_gates is None:
417
+ basis_gates = ["U"]
418
+ return _qasm3.dumps(
419
+ circuit,
420
+ {
421
+ "includes": includes,
422
+ "basis_gates": basis_gates,
423
+ "disable_constants": disable_constants,
424
+ "alias_classical_registers": alias_classical_registers,
425
+ "allow_aliasing": allow_aliasing,
426
+ "indent": indent,
427
+ },
428
+ )
429
+
430
+
431
+ @functools.wraps(_qasm3.dump)
432
+ def dump_experimental(
433
+ circuit,
434
+ stream,
435
+ *,
436
+ includes=None,
437
+ basis_gates=None,
438
+ disable_constants=True,
439
+ alias_classical_registers=False,
440
+ allow_aliasing=False,
441
+ indent=" ",
442
+ ):
443
+ """<overridden by functools.wraps>"""
444
+ warnings.warn(
445
+ "This is an experimental version of serialization of a :class:`~qiskit.circuit.QuantumCircuit`"
446
+ " object in an OpenQASM 3 string."
447
+ " Beware that its interface might change, and it might be missing features.",
448
+ category=ExperimentalWarning,
449
+ )
450
+ if includes is None:
451
+ includes = ["stdgates.inc"]
452
+ if basis_gates is None:
453
+ basis_gates = ["U"]
454
+
455
+ return _qasm3.dump(
456
+ circuit,
457
+ stream,
458
+ {
459
+ "includes": includes,
460
+ "basis_gates": basis_gates,
461
+ "disable_constants": disable_constants,
462
+ "alias_classical_registers": alias_classical_registers,
463
+ "allow_aliasing": allow_aliasing,
464
+ "indent": indent,
465
+ },
466
+ )
qiskit/qasm3/ast.py CHANGED
@@ -54,6 +54,16 @@ class Pragma(ASTNode):
54
54
  self.content = content
55
55
 
56
56
 
57
+ class Annotation(ASTNode):
58
+ """An annotation."""
59
+
60
+ __slots__ = ("namespace", "payload")
61
+
62
+ def __init__(self, namespace: str, payload: str):
63
+ self.namespace = namespace
64
+ self.payload = payload
65
+
66
+
57
67
  class CalibrationGrammarDeclaration(Statement):
58
68
  """
59
69
  calibrationGrammarDeclaration
@@ -701,15 +711,19 @@ class WhileLoopStatement(Statement):
701
711
  class BoxStatement(Statement):
702
712
  """Like ``box[duration] { statements* }``."""
703
713
 
704
- __slots__ = ("duration", "body")
714
+ # TODO: the `annotations` field maybe should move to `Statement` if it becomes more generally
715
+ # supported.
716
+ __slots__ = ("annotations", "duration", "body")
705
717
 
706
718
  def __init__(
707
719
  self,
708
720
  body: ProgramBlock,
709
721
  duration: Expression | None = None,
722
+ annotations: Sequence[Annotation] = (),
710
723
  ):
711
724
  self.body = body
712
725
  self.duration = duration
726
+ self.annotations = tuple(annotations)
713
727
 
714
728
 
715
729
  class BreakStatement(Statement):
qiskit/qasm3/exporter.py CHANGED
@@ -27,6 +27,7 @@ from typing import Iterable, List, Sequence, Union
27
27
  from qiskit._accelerate.circuit import StandardGate
28
28
  from qiskit.circuit import (
29
29
  library,
30
+ Annotation,
30
31
  Barrier,
31
32
  CircuitInstruction,
32
33
  Clbit,
@@ -42,6 +43,7 @@ from qiskit.circuit import (
42
43
  Bit,
43
44
  Register,
44
45
  )
46
+ from qiskit.circuit.annotation import iter_namespaces, OpenQASM3Serializer
45
47
  from qiskit.circuit.classical import expr, types
46
48
  from qiskit.circuit.controlflow import (
47
49
  BoxOp,
@@ -136,6 +138,7 @@ class Exporter:
136
138
  allow_aliasing: bool = None,
137
139
  indent: str = " ",
138
140
  experimental: ExperimentalFeatures = ExperimentalFeatures(0),
141
+ annotation_handlers: dict[str, OpenQASM3Serializer] | None = None,
139
142
  ):
140
143
  """
141
144
  Args:
@@ -172,6 +175,10 @@ class Exporter:
172
175
  set to the empty string to disable indentation.
173
176
  experimental: any experimental features to enable during the export. See
174
177
  :class:`ExperimentalFeatures` for more details.
178
+ annotation_handlers: a mapping of namespaces to annotation serializers. When an
179
+ :class:`.Annotation` object is encountered, the most specific namespace in this
180
+ mapping that matches the annotation's :attr:`~.Annotation.namespace` attribute will
181
+ be used to serialize it.
175
182
  """
176
183
  self.basis_gates = basis_gates
177
184
  self.disable_constants = disable_constants
@@ -181,6 +188,7 @@ class Exporter:
181
188
  self.includes = list(includes)
182
189
  self.indent = indent
183
190
  self.experimental = experimental
191
+ self.annotation_handlers = {} if annotation_handlers is None else annotation_handlers
184
192
 
185
193
  def dumps(self, circuit):
186
194
  """Convert the circuit to OpenQASM 3, returning the result as a string."""
@@ -197,6 +205,7 @@ class Exporter:
197
205
  disable_constants=self.disable_constants,
198
206
  allow_aliasing=self.allow_aliasing,
199
207
  experimental=self.experimental,
208
+ annotation_handlers=self.annotation_handlers,
200
209
  )
201
210
  BasicPrinter(stream, indent=self.indent, experimental=self.experimental).visit(
202
211
  builder.build_program()
@@ -225,43 +234,43 @@ _CANONICAL_CONTROLLED_STANDARD_GATES = {
225
234
  # Mapping of symbols defined by `stdgates.inc` to their gate definition source.
226
235
  _KNOWN_INCLUDES = {
227
236
  "stdgates.inc": {
228
- "p": _CANONICAL_STANDARD_GATES[StandardGate.PhaseGate],
229
- "x": _CANONICAL_STANDARD_GATES[StandardGate.XGate],
230
- "y": _CANONICAL_STANDARD_GATES[StandardGate.YGate],
231
- "z": _CANONICAL_STANDARD_GATES[StandardGate.ZGate],
232
- "h": _CANONICAL_STANDARD_GATES[StandardGate.HGate],
233
- "s": _CANONICAL_STANDARD_GATES[StandardGate.SGate],
234
- "sdg": _CANONICAL_STANDARD_GATES[StandardGate.SdgGate],
235
- "t": _CANONICAL_STANDARD_GATES[StandardGate.TGate],
236
- "tdg": _CANONICAL_STANDARD_GATES[StandardGate.TdgGate],
237
- "sx": _CANONICAL_STANDARD_GATES[StandardGate.SXGate],
238
- "rx": _CANONICAL_STANDARD_GATES[StandardGate.RXGate],
239
- "ry": _CANONICAL_STANDARD_GATES[StandardGate.RYGate],
240
- "rz": _CANONICAL_STANDARD_GATES[StandardGate.RZGate],
241
- "cx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CXGate][1],
242
- "cy": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CYGate][1],
243
- "cz": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CZGate][1],
244
- "cp": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CPhaseGate][1],
245
- "crx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRXGate][1],
246
- "cry": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRYGate][1],
247
- "crz": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRZGate][1],
248
- "ch": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CHGate][1],
249
- "swap": _CANONICAL_STANDARD_GATES[StandardGate.SwapGate],
250
- "ccx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CCXGate][3],
251
- "cswap": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CSwapGate][1],
252
- "cu": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CUGate][1],
253
- "CX": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CXGate][1],
254
- "phase": _CANONICAL_STANDARD_GATES[StandardGate.PhaseGate],
255
- "cphase": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CPhaseGate][1],
256
- "id": _CANONICAL_STANDARD_GATES[StandardGate.IGate],
257
- "u1": _CANONICAL_STANDARD_GATES[StandardGate.U1Gate],
258
- "u2": _CANONICAL_STANDARD_GATES[StandardGate.U2Gate],
259
- "u3": _CANONICAL_STANDARD_GATES[StandardGate.U3Gate],
237
+ "p": _CANONICAL_STANDARD_GATES[StandardGate.Phase],
238
+ "x": _CANONICAL_STANDARD_GATES[StandardGate.X],
239
+ "y": _CANONICAL_STANDARD_GATES[StandardGate.Y],
240
+ "z": _CANONICAL_STANDARD_GATES[StandardGate.Z],
241
+ "h": _CANONICAL_STANDARD_GATES[StandardGate.H],
242
+ "s": _CANONICAL_STANDARD_GATES[StandardGate.S],
243
+ "sdg": _CANONICAL_STANDARD_GATES[StandardGate.Sdg],
244
+ "t": _CANONICAL_STANDARD_GATES[StandardGate.T],
245
+ "tdg": _CANONICAL_STANDARD_GATES[StandardGate.Tdg],
246
+ "sx": _CANONICAL_STANDARD_GATES[StandardGate.SX],
247
+ "rx": _CANONICAL_STANDARD_GATES[StandardGate.RX],
248
+ "ry": _CANONICAL_STANDARD_GATES[StandardGate.RY],
249
+ "rz": _CANONICAL_STANDARD_GATES[StandardGate.RZ],
250
+ "cx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CX][1],
251
+ "cy": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CY][1],
252
+ "cz": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CZ][1],
253
+ "cp": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CPhase][1],
254
+ "crx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRX][1],
255
+ "cry": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRY][1],
256
+ "crz": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRZ][1],
257
+ "ch": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CH][1],
258
+ "swap": _CANONICAL_STANDARD_GATES[StandardGate.Swap],
259
+ "ccx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CCX][3],
260
+ "cswap": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CSwap][1],
261
+ "cu": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CU][1],
262
+ "CX": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CX][1],
263
+ "phase": _CANONICAL_STANDARD_GATES[StandardGate.Phase],
264
+ "cphase": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CPhase][1],
265
+ "id": _CANONICAL_STANDARD_GATES[StandardGate.I],
266
+ "u1": _CANONICAL_STANDARD_GATES[StandardGate.U1],
267
+ "u2": _CANONICAL_STANDARD_GATES[StandardGate.U2],
268
+ "u3": _CANONICAL_STANDARD_GATES[StandardGate.U3],
260
269
  },
261
270
  }
262
271
 
263
272
  _BUILTIN_GATES = {
264
- "U": _CANONICAL_STANDARD_GATES[StandardGate.UGate],
273
+ "U": _CANONICAL_STANDARD_GATES[StandardGate.U],
265
274
  }
266
275
 
267
276
 
@@ -563,6 +572,7 @@ class QASM3Builder:
563
572
  disable_constants,
564
573
  allow_aliasing,
565
574
  experimental=ExperimentalFeatures(0),
575
+ annotation_handlers=None,
566
576
  ):
567
577
  self.scope = BuildScope(
568
578
  quantumcircuit,
@@ -582,6 +592,7 @@ class QASM3Builder:
582
592
  self.includes = includeslist
583
593
  self.basis_gates = basis_gates
584
594
  self.experimental = experimental
595
+ self.annotation_handlers = {} if annotation_handlers is None else annotation_handlers
585
596
 
586
597
  @contextlib.contextmanager
587
598
  def new_scope(self, circuit: QuantumCircuit, qubits: Iterable[Qubit], clbits: Iterable[Clbit]):
@@ -991,8 +1002,8 @@ class QASM3Builder:
991
1002
  measurement = ast.QuantumMeasurement(
992
1003
  [self._lookup_bit(operand) for operand in instruction.qubits]
993
1004
  )
994
- qubit = self._lookup_bit(instruction.clbits[0])
995
- nodes = [ast.QuantumMeasurementAssignment(qubit, measurement)]
1005
+ clbit = self._lookup_bit(instruction.clbits[0])
1006
+ nodes = [ast.QuantumMeasurementAssignment(clbit, measurement)]
996
1007
  elif isinstance(instruction.operation, Reset):
997
1008
  nodes = [
998
1009
  ast.QuantumReset(self._lookup_bit(operand)) for operand in instruction.qubits
@@ -1090,10 +1101,13 @@ class QASM3Builder:
1090
1101
  """Build a :class:`.BoxOp` into a :class:`.ast.BoxStatement`."""
1091
1102
  duration = self.build_duration(instruction.operation.duration, instruction.operation.unit)
1092
1103
  body_circuit = instruction.operation.blocks[0]
1104
+ annotations = [
1105
+ self.build_annotation(annotation) for annotation in instruction.operation.annotations
1106
+ ]
1093
1107
  with self.new_scope(body_circuit, instruction.qubits, instruction.clbits):
1094
1108
  # TODO: handle no-op qubits (see https://github.com/openqasm/openqasm/issues/584).
1095
1109
  body = ast.ProgramBlock(self.build_current_scope())
1096
- return ast.BoxStatement(body, duration)
1110
+ return ast.BoxStatement(body, duration, annotations=annotations)
1097
1111
 
1098
1112
  def build_while_loop(self, instruction: CircuitInstruction) -> ast.WhileLoopStatement:
1099
1113
  """Build a :obj:`.WhileLoopOp` into a :obj:`.ast.WhileLoopStatement`."""
@@ -1129,6 +1143,15 @@ class QASM3Builder:
1129
1143
  body_ast = ast.ProgramBlock(self.build_current_scope())
1130
1144
  return ast.ForLoopStatement(indexset_ast, loop_parameter_ast, body_ast)
1131
1145
 
1146
+ def build_annotation(self, annotation: Annotation) -> ast.Annotation:
1147
+ """Use the custom serializers to construct an annotation object."""
1148
+ for namespace in iter_namespaces(annotation.namespace):
1149
+ if (serializer := self.annotation_handlers.get(namespace, None)) is not None and (
1150
+ payload := serializer.dump(annotation)
1151
+ ) is not NotImplemented:
1152
+ return ast.Annotation(annotation.namespace, payload)
1153
+ raise QASM3ExporterError(f"No configured annotation serializer could handle {annotation}")
1154
+
1132
1155
  def _lookup_variable_for_expression(self, var):
1133
1156
  if isinstance(var, Bit):
1134
1157
  return self._lookup_bit(var)
qiskit/qasm3/printer.py CHANGED
@@ -205,6 +205,13 @@ class BasicPrinter:
205
205
  def _visit_Pragma(self, node: ast.Pragma) -> None:
206
206
  self._write_statement(f"#pragma {node.content}")
207
207
 
208
+ def _visit_Annotation(self, node: ast.Annotation) -> None:
209
+ self._start_line()
210
+ self.stream.write(f"@{node.namespace}")
211
+ if node.payload:
212
+ self.stream.write(f" {node.payload}")
213
+ self._end_line()
214
+
208
215
  def _visit_CalibrationGrammarDeclaration(self, node: ast.CalibrationGrammarDeclaration) -> None:
209
216
  self._write_statement(f'defcalgrammar "{node.name}"')
210
217
 
@@ -597,6 +604,11 @@ class BasicPrinter:
597
604
  self.stream.write("default")
598
605
 
599
606
  def _visit_BoxStatement(self, node: ast.BoxStatement) -> None:
607
+ # The OpenQASM 3 spec doesn't specify any ordering between annotations. We choose to
608
+ # write and interpret them like Python decorators, where the "first" annotation is written
609
+ # closest to the box itself.
610
+ for annotation in reversed(node.annotations):
611
+ self.visit(annotation)
600
612
  self._start_line()
601
613
  self.stream.write("box")
602
614
  if node.duration is not None:
qiskit/qpy/__init__.py CHANGED
@@ -99,6 +99,7 @@ API documentation
99
99
 
100
100
  .. autofunction:: load
101
101
  .. autofunction:: dump
102
+ .. autofunction:: get_qpy_version
102
103
 
103
104
  These functions will raise a custom subclass of :exc:`.QiskitError` if they encounter problems
104
105
  during serialization or deserialization.
@@ -139,6 +140,10 @@ load that QPY file with qiskit-terra 0.19.0 and a hypothetical qiskit-terra
139
140
  0.29.0. However, loading that QPY file with 0.18.0 is not supported and may not
140
141
  work.
141
142
 
143
+ Note that circuit metadata and custom :class:`.Annotation` objects are serialized and deserialized
144
+ by user-supplied classes, as the objects themselves are completely user-custom, so the forwards- and
145
+ backwards-compatibility of these is limited by what the user provides.
146
+
142
147
  If a feature being loaded is deprecated in the corresponding qiskit release, QPY will
143
148
  raise a :exc:`~.QPYLoadingDeprecatedFeatureWarning` informing of the deprecation period
144
149
  and how the feature will be internally handled.
@@ -194,9 +199,21 @@ of QPY in qiskit-terra 0.18.0.
194
199
  * - Qiskit (qiskit-terra for < 1.0.0) version
195
200
  - :func:`.dump` format(s) output versions
196
201
  - :func:`.load` maximum supported version (older format versions can always be read)
202
+ * - 2.1.0
203
+ - 13, 14, 15
204
+ - 15
205
+ * - 2.0.2
206
+ - 13, 14
207
+ - 14
208
+ * - 2.0.1
209
+ - 13, 14
210
+ - 14
197
211
  * - 2.0.0
198
212
  - 13, 14
199
213
  - 14
214
+ * - 1.4.3
215
+ - 10, 11, 12, 13
216
+ - 13
200
217
  * - 1.4.2
201
218
  - 10, 11, 12, 13
202
219
  - 13
@@ -387,17 +404,175 @@ All values use network byte order [#f1]_ (big endian) for cross platform
387
404
  compatibility.
388
405
 
389
406
  The file header is immediately followed by the circuit payloads.
390
- Each individual circuit is composed of the following parts:
407
+ Each individual circuit is composed of the following parts in order from top to bottom:
408
+
409
+ .. code-block:: text
391
410
 
392
- ``HEADER | METADATA | REGISTERS | STANDALONE_VARS | CUSTOM_DEFINITIONS | INSTRUCTIONS``
411
+ HEADER
412
+ METADATA
413
+ REGISTERS
414
+ ANNOTATION_HEADER
415
+ STANDALONE_VARS
416
+ CUSTOM_DEFINITIONS
417
+ INSTRUCTIONS
393
418
 
394
- The ``STANDALONE_VARS`` are new in QPY version 12; before that, there was no data between
395
- ``REGISTERS`` and ``CUSTOM_DEFINITIONS``.
419
+ .. versionchanged:: QPY 15
420
+ ``ANNOTATION_HEADER`` was added between ``REGISTERS`` and ``STANDALONE_VARS``.
421
+
422
+ .. versionchanged:: QPY 12
423
+ ``STANDALONE_VARS`` was added between ``REGISTERS`` and ``CUSTOM_DEFINITIONS``.
396
424
 
397
425
  There is a circuit payload for each circuit (where the total number is dictated
398
426
  by ``num_circuits`` in the file header). There is no padding between the
399
427
  circuits in the data.
400
428
 
429
+ .. _qpy_version_15:
430
+
431
+ Version 15
432
+ ----------
433
+
434
+ Version 15 adds the concept of custom annotations to the payload format. QPY itself does not
435
+ specify how annotations are serialized or deserialized, as they are custom user objects. The format
436
+ does co-operate with sub-serializers, however.
437
+
438
+ Version 15 adds the ``ANNOTATION_HEADER`` field between the ``STANDALONE_VARS`` and
439
+ ``CUSTOM_DEFINITIONS`` fields in the top level of a single circuit payload. It modifies the
440
+ interpretation of one field of the ``INSTRUCTION`` struct in an ABI-compatible manner, and adds a
441
+ ``INSTRUCTION_ANNOTATIONS`` trailer to ``INSTRUCTION`` which is present conditional on a set bit in
442
+ the ``INSTRUCTION`` payload.
443
+
444
+ New ANNOTATION_HEADER
445
+ ~~~~~~~~~~~~~~~~~~~~~
446
+
447
+ The ``ANNOTATION_HEADER`` field is a variable-size payload in the header. It begins with an
448
+ instance of ``ANNOTATION_HEADER_STATIC``, which is the C struct:
449
+
450
+ .. code-block:: c
451
+
452
+ struct ANNOTATION_HEADER_STATIC {
453
+ uint32_t num_namespaces;
454
+ }
455
+
456
+ This is immediately followed by ``num_namespaces`` instances of the ``ANNOTATION_STATE`` payload.
457
+ The order of these is important and should be retained during the deserialization process, as
458
+ subsequent ``INSTRUCTION_ANNOTATION`` payloads will index into it.
459
+
460
+ The ``ANNOTATION_STATE`` payload begins with the fixed C struct:
461
+
462
+ .. code-block:: c
463
+
464
+ struct ANNOTATION_STATE_HEADER {
465
+ uint32_t namespace_size;
466
+ uint64_t state_size;
467
+ }
468
+
469
+ This header is immediately followed by ``namespace_size`` bytes of UTF-8 encoded text, which
470
+ comprise the namespace. Those bytes are immediately followed by ``state_size`` bytes of arbitrary
471
+ data. The format of this "state" payload is not defined by QPY. Instead, it is the responsibility
472
+ of an external object associated with the stored namespace. The format does not dictate how to
473
+ produce these objects; as annotations are entirely custom, the user must supply the serialization
474
+ and deserialization methods.
475
+
476
+
477
+ .. _qpy_instruction_v15:
478
+
479
+ Changes to INSTRUCTION
480
+ ~~~~~~~~~~~~~~~~~~~~~~
481
+
482
+ The ``INSTRUCTION`` struct is modified in an ABI compatible manner to :ref:`its previous definition
483
+ in version 9 <qpy_instruction_v9>`. The new struct is the C struct (recall that there is no padding
484
+ between any fields, nor at the end of the struct):
485
+
486
+ .. code-block:: c
487
+
488
+ struct INSTRUCTION {
489
+ uint16_t name_size;
490
+ uint16_t label_size;
491
+ uint16_t num_parameters;
492
+ uint32_t num_qargs;
493
+ uint32_t num_cargs;
494
+ uint8_t extras_key;
495
+ uint16_t conditional_reg_name_size;
496
+ int64_t conditional_value;
497
+ uint32_t num_ctrl_qubits;
498
+ uint32_t ctrl_state;
499
+ }
500
+
501
+ where the field ``uint8_t extras_key`` replaces the previous ``uint8_t conditional_key``. The
502
+ difference is purely in interpretation. The low two bits of the byte are still interpreted as
503
+ defining the condition and its type. The high bit of the byte is now a flag, indicated whether an
504
+ ``INSTRUCTION_ANNOTATIONS_HEADER`` field is present (if the bit is set) in the trailing data of the
505
+ ``INSTRUCTION`` struct.
506
+
507
+ A complete instruction payload appears in the data stream, including trailing objects and without
508
+ any padding bytes inbetween elements, as:
509
+
510
+ .. code-block:: text
511
+
512
+ struct INSTRUCTION;
513
+ uint8_t name[name_size];
514
+ uint8_t label[label_size];
515
+ uint8_t register[conditional_reg_name_size]; (1)
516
+ struct INSTRUCTION_PARAM; (2)
517
+ struct INSTRUCTION_ARG[num_qargs];
518
+ struct INSTRUCTION_ARG[num_cargs];
519
+ struct INSTRUCTION_PARAM[num_parameters];
520
+ INSTRUCTION_ANNOTATIONS; (3)
521
+
522
+ The following notes apply:
523
+
524
+ 1. if the two low bits of the ``extras_key`` have the value ``2``, indicating the condition is an
525
+ ``EXPRESSION``, the ``conditional_reg_name_size`` is always zero.
526
+ 2. this field is present if and only if the two low bits of the ``extras_key`` have the value ``2``,
527
+ indicating the condition is an ``EXPRESSION``.
528
+ 3. this field is present if and only if the high bit of the ``extras_key`` is set. This field has
529
+ a variable size; see :ref:`qpy_instruction_annotations_v15`.
530
+
531
+ .. _qpy_instruction_annotations_v15:
532
+
533
+ New INSTRUCTION_ANNOTATIONS
534
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
535
+
536
+ The ``INSTRUCTION_ANNOTATIONS`` payload begins with the C struct:
537
+
538
+ .. code-block:: c
539
+
540
+ struct INSTRUCTION_ANNOTATIONS_HEADER {
541
+ uint32_t num_annotations;
542
+ }
543
+
544
+ This payload is immediately followed by ``num_annotations`` instances of the
545
+ ``INSTRUCTION_ANNOTATION`` payload, which is of a variable size.
546
+
547
+ The ``INSRTUCTION_ANNOTATION`` payload is defined by the following C struct plus a trailing number
548
+ of bytes equal to the ``payload_size``, called ``ANNOTATION_PAYLOAD``.
549
+
550
+ .. code-block:: c
551
+
552
+ struct INSTRUCTION_ANNOTATION {
553
+ uint32_t namespace_index;
554
+ uint32_t payload_size;
555
+ }
556
+
557
+ The ``namespace_index`` is an integer index into the list of defined ``ANNOTATION_NAMESPACE``
558
+ objects in the ``ANNOTATION_HEADER``. The serialization namespace for an annotation is the UTF-8
559
+ encoded string in the relevant payload.
560
+
561
+ The format of the ``ANNOTATION_PAYLOAD`` object is not specified by QPY. It is defined by an
562
+ external serialization object associated with the namespace referred to by the ``namespace_index``
563
+ and its associated serializer state in the ``ANNOTATION_HEADER``.
564
+
565
+
566
+ Changes within PARAM_EXPR_ELEM_V13
567
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
568
+
569
+ The struct itself is unchanged. However, for a ``PARAM_EXPR_ELEM_V13`` representing a
570
+ :meth:`.ParameterExpression.subs` call (with ``op_code = 15``, and therefore ``lhs_type = 'p'`` and
571
+ ``rhs_type = 'n'``), the trailing :ref:`qpy_mapping` now maps keys of the raw bytes of the
572
+ :class:`.Parameter` UUIDs to the substituted values. Previously (in QPY versions 13 and 14), this
573
+ mapping stored the parameter names as the keys.
574
+
575
+
401
576
  .. _qpy_version_14:
402
577
 
403
578
  Version 14
@@ -900,6 +1075,7 @@ Python type Type code Payload
900
1075
  =========== ========= ============================================================================
901
1076
 
902
1077
 
1078
+ .. _qpy_instruction_v9:
903
1079
 
904
1080
  Changes to INSTRUCTION
905
1081
  ~~~~~~~~~~~~~~~~~~~~~~
@@ -941,7 +1117,7 @@ Value Effects
941
1117
 
942
1118
  2 The instruction has its ``.condition`` field set to a :class:`~.expr.Expr` node. The
943
1119
  ``conditional_reg_name_size`` and ``conditional_value`` fields should be ignored. The data
944
- following the struct is followed (as in QPY versions less than 8) by ``name_size`` bytes of
1120
+ following the struct is followed (as in QPY versions less than 9) by ``name_size`` bytes of
945
1121
  UTF-8 string data for the class name and ``label_size`` bytes of UTF-8 string data for the
946
1122
  label (if any). Then, there is one INSTRUCTION_PARAM, which will contain an EXPRESSION. After
947
1123
  that, parsing continues with the INSTRUCTION_ARG structs, as in previous versions of QPY.
@@ -1952,7 +2128,7 @@ References
1952
2128
  """
1953
2129
 
1954
2130
  from .exceptions import QpyError, UnsupportedFeatureForVersion, QPYLoadingDeprecatedFeatureWarning
1955
- from .interface import dump, load
2131
+ from .interface import dump, load, get_qpy_version
1956
2132
 
1957
2133
  # For backward compatibility. Provide, Runtime, Experiment call these private functions.
1958
2134
  from .binary_io import (