iqm-pulse 9.20.0__tar.gz → 10.0.0__tar.gz

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 (84) hide show
  1. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/CHANGELOG.rst +22 -2
  2. {iqm_pulse-9.20.0/src/iqm_pulse.egg-info → iqm_pulse-10.0.0}/PKG-INFO +1 -1
  3. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/API.rst +1 -1
  4. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/concepts.rst +27 -23
  5. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/conf.py +1 -0
  6. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/custom_gates.rst +72 -68
  7. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/pulse_timing.rst +10 -10
  8. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/using_builder.rst +29 -23
  9. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/builder.py +193 -92
  10. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/circuit_operations.py +0 -5
  11. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gate_implementation.py +242 -100
  12. iqm_pulse-10.0.0/src/iqm/pulse/gates/__init__.py +240 -0
  13. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/conditional.py +3 -1
  14. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/cz.py +7 -15
  15. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/default_gates.py +17 -117
  16. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/flux_multiplexer.py +2 -2
  17. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/measure.py +46 -41
  18. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/prx.py +0 -5
  19. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/reset.py +8 -16
  20. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/rz.py +1 -1
  21. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/sx.py +1 -1
  22. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/u.py +1 -1
  23. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/instructions.py +7 -7
  24. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/quantum_ops.py +29 -31
  25. iqm_pulse-10.0.0/src/iqm/pulse/utils.py +109 -0
  26. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0/src/iqm_pulse.egg-info}/PKG-INFO +1 -1
  27. iqm_pulse-10.0.0/version.txt +1 -0
  28. iqm_pulse-9.20.0/src/iqm/pulse/gates/__init__.py +0 -356
  29. iqm_pulse-9.20.0/src/iqm/pulse/utils.py +0 -201
  30. iqm_pulse-9.20.0/version.txt +0 -1
  31. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/LICENSE.txt +0 -0
  32. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/MANIFEST.in +0 -0
  33. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/README.rst +0 -0
  34. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/Makefile +0 -0
  35. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/.gitignore +0 -0
  36. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/css/custom.css +0 -0
  37. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/images/favicon.ico +0 -0
  38. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/images/feedback_timing.svg +0 -0
  39. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/images/logo.png +0 -0
  40. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/images/playlist_breakdown.svg +0 -0
  41. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/images/pulse_timing.svg +0 -0
  42. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_static/images/readout_timing.svg +0 -0
  43. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_templates/autosummary-class-template.rst +0 -0
  44. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/_templates/autosummary-module-template.rst +0 -0
  45. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/changelog.rst +0 -0
  46. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/index.rst +0 -0
  47. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/license.rst +0 -0
  48. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/references.bib +0 -0
  49. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/docs/references.rst +0 -0
  50. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/pyproject.toml +0 -0
  51. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/requirements/base.in +0 -0
  52. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/requirements/base.txt +0 -0
  53. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/setup.cfg +0 -0
  54. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/setup.py +0 -0
  55. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/__init__.py +0 -0
  56. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/base_utils.py +0 -0
  57. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/barrier.py +0 -0
  58. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/delay.py +0 -0
  59. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/enums.py +0 -0
  60. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/gates/move.py +0 -0
  61. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/__init__.py +0 -0
  62. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/channel.py +0 -0
  63. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/fast_drag.py +0 -0
  64. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/hd_drag.py +0 -0
  65. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/playlist.py +0 -0
  66. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/schedule.py +0 -0
  67. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/__init__.py +0 -0
  68. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/base.py +0 -0
  69. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/templates/playlist_inspection.jinja2 +0 -0
  70. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png +0 -0
  71. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/templates/static/moment.min.js +0 -0
  72. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css +0 -0
  73. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js +0 -0
  74. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/playlist/waveforms.py +0 -0
  75. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/py.typed +0 -0
  76. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/scheduler.py +0 -0
  77. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/timebox.py +0 -0
  78. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm/pulse/validation.py +0 -0
  79. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm_pulse.egg-info/SOURCES.txt +0 -0
  80. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm_pulse.egg-info/dependency_links.txt +0 -0
  81. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm_pulse.egg-info/requires.txt +0 -0
  82. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/src/iqm_pulse.egg-info/top_level.txt +0 -0
  83. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/tests/.pylintrc +0 -0
  84. {iqm_pulse-9.20.0 → iqm_pulse-10.0.0}/tests/__init__.py +0 -0
@@ -2,6 +2,26 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 10.0.0 (2025-07-16)
6
+ ===========================
7
+
8
+ Breaking changes
9
+ ----------------
10
+
11
+ - :class:`.CompositeGate` subclasses must now include all their member gates in :attr:`.CompositeGate.registered_gates`.
12
+ The subclasses should apply the member gates using :meth:`.CompositeGate.build`.
13
+ - Removed the :meth:`.PRX_SinglePulse_GateImplementation.iq_pulse` alias, use ``.pulse`` instead.
14
+ - :func:`.register_implementation` no longer can register or define a QuantumOp.
15
+ :func:`.register_operation` is introduced for that purpose.
16
+
17
+ Version 9.21.0 (2025-07-10)
18
+ ===========================
19
+
20
+ Bug fixes
21
+ ---------
22
+
23
+ - Fix instructions with same field names being treated as equal in building the playlist
24
+
5
25
  Version 9.20.0 (2025-07-09)
6
26
  ===========================
7
27
 
@@ -192,7 +212,7 @@ Version 8.12.0 (2025-04-03)
192
212
  ===========================
193
213
 
194
214
  Feature
195
- *******
215
+ -------
196
216
 
197
217
  - Format code and enable PEP 604 in linting rules, :issue:`SW-1230`.
198
218
 
@@ -209,7 +229,7 @@ Version 8.10.0 (2025-04-02)
209
229
  ===========================
210
230
 
211
231
  Features
212
- ********
232
+ --------
213
233
 
214
234
  - Update the documentation footer to display the package version.
215
235
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-pulse
3
- Version: 9.20.0
3
+ Version: 10.0.0
4
4
  Summary: A Python-based project for providing interface and implementations for control pulses.
5
5
  Author-email: IQM Finland Oy <info@meetiqm.com>
6
6
  License: Apache License
@@ -1,4 +1,4 @@
1
- API Reference
1
+ API reference
2
2
  =============
3
3
 
4
4
  .. autosummary::
@@ -1,4 +1,4 @@
1
- Concepts and Classes
1
+ Concepts and classes
2
2
  ####################
3
3
 
4
4
  This section gives an overview of the main concepts and terminology in IQM Pulse.
@@ -20,7 +20,7 @@ The assembly of a Playlist, or a batch of quantum circuits, can be summarized as
20
20
  assumed to be preserved. A quantum circuit corresponds to one segment.
21
21
  * What is executed during a segment is determined by a **Schedule**.
22
22
  * A Schedule is a set of hardware control channels, each with a strictly timed sequence of **Instructions**.
23
- * A Schedule is formed by scheduling a **Timebox**.
23
+ * A Schedule is formed by scheduling a number of **Timeboxes**.
24
24
  * A TimeBox can contain other TimeBoxes without precise relative timing,
25
25
  or it can be atomic, in which case it contains a single **Schedule**.
26
26
 
@@ -68,7 +68,7 @@ Instead, the magic happens on the client side so that it is transparent to the u
68
68
  Schedules
69
69
  ---------
70
70
 
71
- :class:`~iqm.pulse.playlist.schedule.Schedule` contains a number of control channels, each with a lists of Instructions.
71
+ :class:`~iqm.pulse.playlist.schedule.Schedule` contains a number of control channels, each with a list of Instructions.
72
72
  All channels in a Schedule start executing at the same instant, and the timing is defined by the duration of the
73
73
  individual Instructions.
74
74
  Schedules can be thought of as a fixed block that occupies some interval on a timeline of some channels.
@@ -87,7 +87,7 @@ Whereas a Schedule is a container with strict relative timing, a :class:`.TimeBo
87
87
  relative timing.
88
88
  Each TimeBox can be labeled using a human-readable label describing it, and operates on a number
89
89
  of *locus components*, using some of their control channels.
90
- A composite TimeBox contains other TimeBoxes as children, whereas atomic TimeBoxes contain a Schedule.
90
+ A composite TimeBox contains other TimeBoxes as children, whereas atomic TimeBoxes contain a single Schedule.
91
91
 
92
92
  TimeBoxes are the main language in which users define the order and relative alignment of execution elements, be it
93
93
  gates, Schedules, or larger TimeBoxes.
@@ -95,10 +95,10 @@ gates, Schedules, or larger TimeBoxes.
95
95
  A key process is the scheduling, in which TimeBoxes are resolved recursively into a fixed Schedule.
96
96
  When resolving, all Schedules inside the TimeBox are concatenated and are either left-aligned (ASAP) or right-aligned
97
97
  (ALAP), respecting the hardware constraints.
98
- Importantly, if some TimeBoxes have content on disjoint channels, the Schedules are allowed to happen simultaneously.
98
+ Importantly, if some TimeBoxes have content on disjoint channels, their Schedules are allowed to happen simultaneously.
99
99
  If they have content on partly overlapping channels, the Schedules are concatenated while preserving their internal
100
100
  timing.
101
- Any interval that does not have explicit instructions is filled with Wait Instructions.
101
+ Any interval that does not have explicit instructions is filled with Wait instructions.
102
102
  The figure above demonstrates how TimeBoxes are resolved.
103
103
 
104
104
  The syntax and rules are explained in more detail in :doc:`using_builder`.
@@ -106,56 +106,60 @@ The syntax and rules are explained in more detail in :doc:`using_builder`.
106
106
  QuantumOps
107
107
  ----------
108
108
 
109
- A higher-level concept, a :class:`.QuantumOp` can represent a unitary quantum gate,
110
- or for example a measurement operation (not all QuantumOps necessarily represent a unitary gate).
111
- QuantumOps are simple, abstract, self-contained actions one can execute on a station as parts of a quantum circuit.
112
- They include quantum gates like PRX, CZ, and measurements and resets.
113
- Whereas Schedules and Instructions act on control channels, QuantumOps act on named components on the QPU, such as
114
- qubits or computational resonators.
109
+ A higher-level concept, a :class:`.QuantumOp` (quantum operation) can represent a unitary quantum gate like PRX or CZ,
110
+ or a nonunitary operation like a measurement or a reset.
111
+ QuantumOps are simple, abstract, self-contained actions one can apply on the quantum state of the QPU
112
+ as parts of a quantum circuit.
113
+ Whereas Schedules and Instructions act on control channels, QuantumOps act on *loci* which are ordered sequences of
114
+ QPU components, such as qubits or computational resonators.
115
115
 
116
- A QuantumOp has unambiguous definition in terms of its *intended* effect on the computational subspace of the
117
- QPU component, but it can be *implemented* in various ways.
116
+ A QuantumOp has an unambiguous definition in terms of its *intended* effect on the computational subspace of its
117
+ locus components, but it can be *implemented* on a station in various ways.
118
118
  Each implementation is represented as a GateImplementation.
119
119
 
120
120
  The list of available QuantumOps at runtime can be obtained with :func:`iqm.pulse.builder.build_quantum_ops`.
121
- A new QuantumOp can be registered at runtime, together with an implementation, with
122
- :func:`iqm.pulse.gates.register_implementation`.
121
+ A new QuantumOp can be registered at runtime using :func:`iqm.pulse.gates.register_operation`.
123
122
 
124
123
  GateImplementations
125
124
  -------------------
126
125
 
127
126
  A :class:`.GateImplementation` bridges the gap between QuantumOps and TimeBoxes.
127
+ It represents the concrete control signals sent to the station in order to apply a QuantumOp.
128
+ Despite the name, GateImplementations are used to implement all QuantumOps, not just unitary quantum gates.
129
+
128
130
  When a user requests a QuantumOp from :class:`.ScheduleBuilder` with specific parameters and locus components, the
129
131
  chosen GateImplementation (usually the default) for the operation is used to produce a TimeBox.
130
132
  This TimeBox, usually atomic, contains a Schedule on the appropriate control channels.
131
- The Instructions within are constructed following the calibration values from the ScheduleBuilder.
133
+ The Instructions within are constructed using the calibration values for that operation, implementation and locus
134
+ from the ScheduleBuilder.
132
135
 
133
136
  All gate implementations are listed in :mod:`iqm.pulse.gates`.
134
137
  Section :doc:`custom_gates` explains how to add more implementations.
135
-
138
+ A new GateImplementation can be added to a known (registered) QuantumOp using
139
+ :func:`iqm.pulse.gates.register_implementation`.
136
140
 
137
141
  Playlists
138
142
  ---------
139
143
 
140
144
  Once all TimeBoxes are scheduled into large Schedules, one for each segment/circuit,
141
- the Schedules are collected into a :class:`.Playlist`.
145
+ the Schedules are collected into a :class:`~iqm.models.playlist.playlist.Playlist`.
142
146
  The Playlist is the final product that is sent to Station Control.
143
147
  Its contents are compressed by indexing all unique Instructions and waveforms on each channel,
144
148
  and representing the control channels in each segment as lists of Instruction indices.
145
149
 
146
150
  During execution, the segments in the Playlist are executed in order, and the whole sequence is repeated
147
- a number of times equal to the number of repetitions (shots).
151
+ a number of times equal to the number of repetitions (shots) requested.
148
152
 
149
153
  Segments are separated in time by **end delay**, a parameter outside the Playlist.
150
154
  A long end delay can be used to prevent quantum information carrying from one segment to the next,
151
155
  thus resetting the qubits.
152
156
  Alternatively, the reset can be encoded in each segment as a long Wait instruction or using some active reset scheme.
153
157
 
154
- Station Control aims to execute all segments together, but sometimes this is not possible due to various memory
155
- constraints.
158
+ Station Control aims to execute all segments one after another in one go, but sometimes this is not
159
+ possible due to various memory constraints.
156
160
  In case the whole Playlist does not fit in memory, the segments are split into chunks which are executed separately.
157
161
  The delay between chunks is undefined.
158
162
  Therefore, the time between segments is guaranteed to be at least the duration of the end delay, but can be much larger.
159
163
 
160
164
  :func:`.inspect_playlist` provides a neat visual representation of the playlist, as blocks of instructions on a
161
- timeline.
165
+ timeline.
@@ -183,6 +183,7 @@ intersphinx_mapping = {
183
183
  "scipy": ("https://docs.scipy.org/doc/scipy-1.6.3/reference", None),
184
184
  "xarray": ("https://docs.xarray.dev/en/stable", None),
185
185
  "exa.common": ("../exa-common", "../../exa-common/build/sphinx/objects.inv"),
186
+ "iqm.models": ("../iqm-data-definitions", "https://iqm.gitlab-pages.iqm.fi/qccsw/iqm-data-definitions/objects.inv"),
186
187
  }
187
188
 
188
189
  use_local_target = os.getenv("USE_LOCAL_TARGET", "").lower()
@@ -4,29 +4,29 @@ Custom gate implementations
4
4
  QuantumOp
5
5
  ---------
6
6
 
7
- Quantum gates are represented by :class:`~iqm.pulse.quantum_ops.QuantumOp` data classes, containing the required
8
- metadata to define the gate. A QuantumOp is identified by its :attr:`~iqm.pulse.quantum_ops.QuantumOp.name`, and
9
- :attr:`~iqm.pulse.quantum_ops.QuantumOp.arity` defines number of locus components the operation acts on. For example,
10
- the PRX operation (Phased X Rotation) is a single-qubit operation, so its arity is 1, whereas the CZ (Controlled-Z) gate
7
+ Quantum gates are represented by :class:`.QuantumOp` data classes, containing the required
8
+ metadata to define the gate. A QuantumOp is identified by its :attr:`~.QuantumOp.name`, and
9
+ :attr:`.QuantumOp.arity` defines number of locus components the operation acts on. For example,
10
+ the ``prx`` operation (Phased X Rotation) is a single-qubit operation, so its arity is 1, whereas the ``cz`` (Controlled-Z) gate
11
11
  acts on two qubits, having arity 2. Arity 0 has a special meaning that the operation in question can act on any number
12
- of components (for example :class:`~iqm.pulse.gates.barrier.Barrier`).
12
+ of components (for example ``measure`` or ``barrier``).
13
13
 
14
- The attribute :attr:`~iqm.pulse.quantum_ops.QuantumOp.symmetric` defines whether the effect of the quantum operation
14
+ The attribute :attr:`.QuantumOp.symmetric` defines whether the effect of the quantum operation
15
15
  is symmetric with respect to changing the order of its locus components. As an example, the CZ gate is a symmetric
16
16
  two-qubit gate, whereas CNOT (Controlled-NOT) is not symmetric.
17
17
 
18
18
  Some quantum operations are defined as "functions", taking one or more parameters to define the effect. These
19
- arguments are stored in the attribute :attr:`~iqm.pulse.quantum_ops.QuantumOp.params`. As an example, the PRX gate
19
+ arguments are stored in the attribute :attr:`.QuantumOp.params`. As an example, the PRX gate
20
20
  takes two arguments, ``angle`` (the rotation angle with respect to the z-axis of the Bloch sphere), and ``phase``
21
21
  (the rotation phase in the rotating frame). On the other hand, many operations do not require any parameters, in
22
22
  which case this field is an empty tuple (e.g. the CZ gate).
23
23
 
24
24
  A QuantumOp has unambiguous definition in terms of its *intended* effect on the computational subspace of the
25
25
  QPU component, but it can be *implemented* in various ways. Each implementation is represented as a
26
- :class:`~iqm.pulse.gate_implementation.GateImplementation` subclass. A QuantumOp stores its known implementations in the
27
- field :attr:`~iqm.pulse.quantum_ops.QuantumOp.implementations`. Note that even though
28
- :class:`~iqm.pulse.quantum_ops.QuantumOp` is a frozen data class, the implementations dictionary can be modified, e.g.
29
- to add new implementations or to change their order (usually programmatically by some client procedure, but nothing as
26
+ :class:`.GateImplementation` subclass. A QuantumOp stores its known implementations in
27
+ :attr:`.QuantumOp.implementations`. Note that even though
28
+ :class:`.QuantumOp` is a frozen data class, the implementations dictionary can be modified, e.g.
29
+ to add new implementations (usually programmatically by some client procedure, but nothing as
30
30
  such prevents the user from manipulating the contents manually). The default implementation is how the user prefers
31
31
  to implement the operation unless otherwise specified (in effect, this is what will get called in most cases the
32
32
  operation is invoked). In the implementations dict, the default implementation is defined as the first entry.
@@ -35,17 +35,17 @@ QuantumOp contains helpful methods that allow setting and returning the default
35
35
  :meth:`~iqm.pulse.quantum_ops.QuantumOp.get_default_implementation_for_locus`, and
36
36
  :meth:`~iqm.pulse.quantum_ops.QuantumOp.set_default_implementation_for_locus`.
37
37
 
38
- The attribute :attr:`~iqm.pulse.quantum_ops.QuantumOp.unitary` stores a function that can be used to get the unitary
38
+ The attribute :attr:`.QuantumOp.unitary` stores a function that can be used to get the unitary
39
39
  matrix representing the quantum operation in question. The unitary function must have the same arguments
40
- as defined in :attr:`~iqm.pulse.quantum_ops.QuantumOp.params`, such that for each collection of these parameters it
41
- gives the associated unitary matrix. Note that not all QuantumOps necessarily even represent a unitary gate (e.g.
42
- the measure operation is not one), or the exact form of the unitary matrix might not be known. In these cases, the
43
- field can be left ``None``. The unitary does not need to be defined for most of the basic usage of a QuantumOp, but certain
40
+ as defined in :attr:`.QuantumOp.params`, such that for each set of values for these parameters it
41
+ gives the associated unitary matrix. Note that not all QuantumOps represent a unitary gate (e.g.
42
+ the ``measure`` operation is not one), or the exact form of the unitary matrix might not be known. In these cases, the
43
+ field can be left to ``None``. The unitary does not need to be defined for most of the basic usage of a QuantumOp, but certain
44
44
  algorithmic methods (e.g. some implementations of Randomized Benchmarking) may require the unitary matrices to be known,
45
- and such operations that do not define the getter function cannot then be used in these contexts.
45
+ and operations that do not define :attr:`.QuantumOp.unitary` cannot be used in these contexts.
46
46
 
47
- For more information, see the API docs of :class:`~iqm.pulse.quantum_ops.QuantumOp` for the full list of fields needed
48
- to define a quantum operation and the available class methods.
47
+ For more information, see the API docs of :class:`.QuantumOp` for the full list of attributes needed
48
+ to define a quantum operation, and the available methods.
49
49
 
50
50
  Custom gate implementations
51
51
  ---------------------------
@@ -56,7 +56,7 @@ GateImplementation class
56
56
  While :class:`~iqm.pulse.quantum_ops.QuantumOp` represents an abstract quantum operation, its *implementations* contain
57
57
  the concrete logic of how to make that operation happen using QC hardware. Gate implementations are subclasses of
58
58
  :class:`~iqm.pulse.gate_implementation.GateImplementation`. In this section, the main features of that class are
59
- introduced (for a full list of class methods see the API docs), with the emphasis being on how to create your own
59
+ introduced (for a full list of methods see the API docs), with the emphasis being on how to create your own
60
60
  gate implementations.
61
61
 
62
62
  Starting with :meth:`~iqm.pulse.gate_implementation.GateImplementation.__init__`, it is important to note that the init
@@ -73,22 +73,23 @@ methods of all gate implementations must have the exact same signature:
73
73
  builder: ScheduleBuilder
74
74
  ):
75
75
 
76
- Here, ``parent`` is the ``QuantumOp`` this gate implementation implements, and ``name`` is the implementation's name in
76
+ Here, ``parent`` is the :class:`.QuantumOp` this gate implementation implements, and ``name`` is the implementation's name in
77
77
  the dictionary :attr:`~iqm.pulse.quantum_ops.QuantumOp.implementations`. ``locus`` is the set of (usually logical) components
78
78
  the QuantumOp acts on (the size of the locus must be consistent with the ``parent``'s
79
79
  :attr:`~iqm.pulse.quantum_ops.QuantumOp.arity`), while ``calibration_data`` gives the required calibration data values
80
80
  for this implementation and ``locus`` (can be empty in case the implementation needs no calibration data). Finally,
81
- The implementations store a reference to the :class:`~iqm.pulse.builder.ScheduleBuilder` that created it. This is
81
+ the implementations store a reference to the :class:`~iqm.pulse.builder.ScheduleBuilder` that created it. This is
82
82
  because GateImplementations are practically never created manually by calling the init method itself. Instead, one
83
83
  needs a builder and uses :meth:`~iqm.pulse.builder.ScheduleBuilder.get_implementation`.
84
84
 
85
- The responsibility of the init method is to (at least) store the ``calibration_data`` provided from the builder for
86
- further use, but in many cases, one might want to create some intermediate objects like pulses or instructions **from**
87
- that calibration data already at this point. Note that ScheduleBuilder caches its GateImplementations per each locus and
88
- ``calibration_data``, so as long as the calibration is not changed, the code in init will be called just once per locus.
85
+ The responsibility of the init method is to initialize the superclass, but in many cases one might want to create
86
+ and cache some intermediate objects like waveforms or instructions **from**
87
+ that calibration data already at this point. Note that ScheduleBuilder caches the GateImplementation instances it
88
+ creates for each (quantum op, implementation, locus) triplet,
89
+ so as long as the calibration is not changed, the code in init will be called just once for each such triplet.
89
90
 
90
- GateImplementations are Callables, i.e. they implement the `__call__` method. It should take as its arguments at least
91
- the QuantumOpt parameters defined for the ``parent`` in :attr:`~iqm.pulse.quantum_ops.QuantumOp.params`, but in
91
+ GateImplementations are Callables, i.e. they implement the :meth:`__call__` method. It should take as its arguments at least
92
+ the ``parent`` QuantumOp parameters defined in :attr:`~.QuantumOp.params`, but in
92
93
  addition it may have optional extra arguments. The call method should return a :class:`~iqm.pulse.timebox.TimeBox` object
93
94
  that contains the pulses, instructions and other logic required to implement the quantum operation in question. The
94
95
  typical usage of gate implementations then looks like this (See :doc:`using_builder` and :doc:`pulse_timing` for more
@@ -105,13 +106,13 @@ info on scheduling and the ScheduleBuilder):
105
106
  default_box = default_prx_QB1(angle=np.pi, phase=np.pi/2)
106
107
 
107
108
  # the initialization of the impl class and the call can of course be also chained together like this:
108
- default_cz_box = builder..get_implementation("cz", ("QB1", "QB2"))() # CZ has no QuantumOp params!
109
+ default_cz_box = builder.get_implementation("cz", ("QB1", "QB2"))() # CZ has no params
109
110
 
110
- The base class :meth:`~iqm.pulse.gate_implementation.GateImplementation.__call__` method does automatic TimeBox caching based
111
+ The base class :meth:`__call__` method does automatic TimeBox caching based
111
112
  on the unique values of the call arguments, and in many cases, one does not want to reimplement this caching in their own
112
- implementations. For this reason, there is the method ``_call`` which contains just the pure TimeBox creation logic.
113
- Developers can choose to override that instead of ``__call__`` in cases where the call args are hashable python types,
114
- and then they can utilize the default caching of TimeBoxes from the base class.
113
+ implementations. For this reason, there is the method :meth:`~.GateImplementation._call` which contains just the pure TimeBox creation logic.
114
+ Developers should override that instead of :meth:`__call__` in cases where the call args are hashable Python types,
115
+ so they can utilize the caching of TimeBoxes from the base class.
115
116
 
116
117
  When writing a GateImplementation, a developer should consider what parts of the logic should go to the class init and
117
118
  what to the ``__call__`` or ``_call`` method. A general rule of thumb would be that any parts that can be precomputed
@@ -138,18 +139,18 @@ use this exact call method, as this is a simplified example for educational purp
138
139
  )
139
140
 
140
141
  Here, we first create an :class:`.IQPulse` object which is a low-level Instruction. IQPulse
141
- means a "complex pulse" which has two orthogonal components i and q -- this what drive pulses look like in general. In
142
+ means a "complex pulse" which has two orthogonal components I and Q --- this what drive pulses look like in general. In
142
143
  this simplified example, we have hardcoded the pulse waveforms into :class:`.TruncatedGaussian` and
143
- :class:`.TruncatedGaussianDerivative` for the i and q components, respectively (this is a DRAG implementation, so the
144
- q component is the derivative of the i component). The waveforms are parametrized by the ``calibration_data`` for the
145
- given ``locus`` (see the next subsection for more info on Waveforms and calibration data). The PRX QuantumOp param
146
- ``angle`` scales the pulse amplitude linearly (the waveforms are normalized to one), and the param ``phase`` defines relative
144
+ :class:`.TruncatedGaussianDerivative` for the I and Q components, respectively (this is a DRAG implementation, so the
145
+ Q component is the scaled derivative of the I component). The waveforms are parametrized by the ``calibration_data`` for the
146
+ given ``locus`` (see the next subsection for more info on Waveforms and calibration data). The PRX parameter
147
+ ``angle`` scales the pulse amplitude linearly (the waveforms are normalized to one), and the parameter ``phase`` defines relative
147
148
  phase modulation. Then the returned TimeBox is created out of the ``instruction``. Note that
148
149
  since we override ``_call`` here, instead of ``__call__``, so this implementation would utilize the default base class
149
- caching such that the TimeBoxes are cached per unique values of ``(angle, phase)``.
150
+ caching such that the TimeBoxes are cached for unique values of ``(angle, phase)``.
150
151
 
151
152
  Another important concept is a the so called locus mapping of a gate implementation. Locus mappings define on which
152
- loci, i.e. groups of components, a given implementation can be defined. They are used to relay the information which
153
+ loci, i.e. groups of components, a given implementation can act. They are used to relay the information which
153
154
  loci are supported to a client application (e.g. EXA). In addition, the gate implementation itself can programmatically
154
155
  use this information ``self.builder.chip_topology``.
155
156
 
@@ -177,10 +178,10 @@ Instructions, Waveforms and calibration data
177
178
  In order to implement most QuantumOps, one has to physically alter the state of the QPU. This is typically done by playing
178
179
  specified and correctly calibrated pulses via the control electronics (this applies to all typical logical gates such as
179
180
  e.g. PRX or CZ -- non-physcial metaoperations such as Barrier are an exception). In defining these pulses, there are two
180
- levels of abstractions: :class:`.Waveform` and :class:`.Instruction`.
181
+ levels of abstractions: :class:`~iqm.models.playlist.waveforms.Waveform` and :class:`.Instruction`.
181
182
 
182
- Waveform represents the physical form of the control pulse, typically normalized to the interval ``[-1.0, 1.0]``. The
183
- Each :class:`~iqm.models.playlist.waveforms.Waveform` subclass can define any number of waveform parameters as class
183
+ Waveform represents the physical form of the control pulse, typically normalized to the interval ``[-1.0, 1.0]``.
184
+ Each Waveform subclass can define any number of parameters as class
184
185
  attributes, which can be used to programmatically define the waveform. For example, a Gaussian could be defined in terms
185
186
  of the average ``mu`` and spread ``sigma``. A Waveform class then essentially contains just the parameters
186
187
  and a recipe for computing the samples as an ``np.ndarray``. As an example, here is how one writes the Waveform class
@@ -201,10 +202,10 @@ for ``Gaussian``:
201
202
  The Instructions :class:`.RealPulse` and
202
203
  :class:`.IQPulse` allow handling the amplitudes (via the attribute ``scale``) without
203
204
  having to resample the waveform for every different amplitude value. However, one can always choose to include
204
- the amplitude into the sampling and then use ``scale=1``.
205
+ the amplitude into the sampling and use ``scale=1``.
205
206
 
206
207
  The waveform parameters (like ``sigma`` in the above Gaussian) typically require calibration when the Waveform is used
207
- in a quantum gate. However, the GateImplementation usually has other calibrated parameters as well defined in the
208
+ in a quantum gate. The GateImplementation usually also has other calibrated parameters as well defined in the
208
209
  implementation itself. As an example, here are the implementation-level parameters of the default PRX implementation,
209
210
  defined as class attribute:
210
211
 
@@ -243,26 +244,23 @@ To make creating new GateImplementations more comfortable, there are additional
243
244
 
244
245
  :class:`.CompositeGate` allows quick implementation of gates in terms of other gates,
245
246
  using a similar syntax as with creating/scheduling several TimeBoxes together (see :doc:`using_builder`). At it
246
- simplest, a ComposteGate is just the `_call` method:
247
+ simplest, a CompositeGate is just the :meth:`~.GateImplementation._call` method:
247
248
 
248
249
  .. code-block:: python
249
250
 
250
251
  class CompositeHadamard(CompositeGate):
251
252
  """Composite Hadamard that uses PRX"""
252
- registered_gates = ["prx"]
253
- # registering member gates is not mandatory, but allows calibrating them specifically inside _this_ composite
253
+ registered_gates = ("prx",)
254
254
 
255
255
  def _call(self) -> TimeBox:
256
256
  member_prx = self.build("prx", self.locus)
257
257
  return member_prx(np.pi / 2, np.pi / 2 ) + member_prx(np.pi, 0.0)
258
258
 
259
- Here, one could use also ``builder.get_implementation`` instead of
260
- :meth:`~iqm.pulse.gate_implementation.CompositeGate.build`, but the latter allows calibrating the member gates
261
- case specifically for this composite if they are first registered via
262
- :attr:`~iqm.pulse.gate_implementation.CompositeGate.registered_gates` (in this case, there is
263
- just one member, PRX).
259
+ The :meth:`.CompositeGate.build` method is used to access the member gate implementations.
260
+ It allows providing a special calibration for the member gates just for this composite gate.
261
+ In this example there is just one member, ``prx``.
264
262
 
265
- Creating new implementations for the PRX, CZ and Measure gates often means just coming up with new waveforms for the
263
+ Creating new implementations for the ``prx``, ``cz`` and ``measure`` gates often means just coming up with new waveforms for the
266
264
  control pulses. If this is the case, there are helpful base classes that make those implementations into oneliners
267
265
  (outside of defining the Waveforms themselves): :class:`~iqm.pulse.gates.prx.PRX_CustomWaveforms`,
268
266
  :class:`~iqm.pulse.gates.cz.FluxPulseGate`, and :class:`~iqm.pulse.gates.measure.Measure_CustomWaveforms`. Using these
@@ -280,21 +278,21 @@ base classes at its simplest looks like this:
280
278
  """Measure with my cool custom waveforms for the i and q probe pulse components"""
281
279
 
282
280
  All of these classes automatically include the associated Waveform parameters into the calibration parameters of
283
- the implementation itself. There is also a general base class for any gate that implements a single ``IQPulse``
284
- (both PRX_CustomWaveForms and Measure_MyCoolWaveforms actually inherit from it), regardless of the context:
285
- :class:`~iqm.pulse.gate_implementation.CustomIQWaveforms`.
281
+ the implementation itself. There is also a general base class :class:`~iqm.pulse.gate_implementation.CustomIQWaveforms`.
282
+ for any gate whose implementation consists of a single IQPulse
283
+ (both :class:`.PRX_CustomWaveforms` and :class:`.Measure_CustomWaveforms` actually inherit from it).
286
284
 
287
285
 
288
286
  Registering gates and implementations
289
287
  -------------------------------------
290
288
 
291
- Gate definitions (i.e. QuantumOps) are stored in :class:`~iqm.pulse.builder.ScheduleBuilder`'s attribute
292
- ``op_table``. When the builder is created, the ``op_table`` comes preloaded with the all the basic QuantumOps needed for
293
- typical circuit execution and their default implementations. These include e.g. the PRX gate, the CZ gate, the measure
294
- operation, the conditional prx operation, the reset operation, and the barrier operation.
289
+ Gate definitions (i.e. QuantumOps) are stored in :attr:`.ScheduleBuilder.op_table`.
290
+ When the builder is created, the ``op_table`` comes preloaded with the all the basic QuantumOps needed for
291
+ typical circuit execution and their default implementations. These include e.g. the ``prx`` gate, the ``cz`` gate, the ``measure``
292
+ operation, the conditional PRX operation ``cc_prx``, the ``reset`` operation, and the ``barrier`` operation.
295
293
 
296
- In order to add custom operations, there is a helpful function :func:`~iqm.pulse.gates.register_implementation` that
297
- in addition to adding new implementations allows one to add altogether new quantum operations.
294
+ New quantum operations can be registered using :func:`~iqm.pulse.gates.register_operation`.
295
+ For adding implementations to the operations, there is :func:`~iqm.pulse.gates.register_implementation`.
298
296
 
299
297
  As an example here is a snippet that adds the CNOT gate, and its implementation, into an existing builder:
300
298
 
@@ -306,19 +304,25 @@ As an example here is a snippet that adds the CNOT gate, and its implementation,
306
304
  [0, 0, 1, 0]], dtype=complex)
307
305
  cnot_op = QuantumOp(name="cnot", arity=2, symmetric=False, unitary=lambda: cnot_matrix)
308
306
 
307
+ register_operation(
308
+ operations=my_builder.op_table,
309
+ op=cnot_op,
310
+ )
309
311
  register_implementation(
310
312
  operations=my_builder.op_table,
311
- gate_name="cnot",
313
+ op_name="cnot"
312
314
  impl_name="my_cnot_impl",
313
315
  impl_class=MyCNotClass,
314
- quantum_op_specs=cnot_op
315
316
  )
316
317
 
317
- Here, the CNOT implementation ``MyCNotClass`` needs to be of course defined first (a QuantumOp always needs at least one
318
+ Here, the CNOT implementation ``MyCNotClass`` needs to be defined first (a QuantumOp always needs at least one
318
319
  implementation).
319
320
 
320
- **Note:** The end user cannot modify the canonical mapping (defined in iqm-pulse) between ``implementation_name`` and
321
- ``implementation_class``.
321
+ .. note::
322
+
323
+ Certain implementation names are *canonical* for certain operations. This means they always map to the same iqm-pulse
324
+ GateImplementation class, and the user cannot modify this mapping, defined in
325
+ :attr:`iqm.pulse.gates.default_gates._default_implementations`.
322
326
 
323
327
  Note that often :class:`.ScheduleBuilder` is created and operated by some client application, and the same application usually
324
328
  has its own interface for adding/manipulating QuantumOps. However, if the user has access to the builder object, the
@@ -6,19 +6,19 @@ Measure and ReadoutTrigger
6
6
 
7
7
  The :class:`~iqm.pulse.playlist.instructions.ReadoutTrigger` Instruction responsible of qubit readout has several
8
8
  timing-related attributes.
9
- The ``measure.constant`` gate implementation produces the lower-level ReadoutTrigger instruction
10
- from a simplified set of settings.
11
- The figure below shows how the settings relate to the more flexible attributes of the instruction.
9
+ The ``measure.constant`` gate implementation produces a ReadoutTrigger instruction
10
+ from a simplified set of parameters.
11
+ The figure below shows how these parameters relate to the more flexible attributes of the instruction.
12
12
 
13
13
  .. image:: /_static/images/readout_timing.svg
14
14
 
15
15
  Fast feedback timing
16
16
  --------------------
17
17
 
18
- With conditional Instructions, we specify how the information from readout operations should affect Instructions at
19
- runtime.
18
+ With conditional instructions, we can specify how the output from readout operations should affect
19
+ other instructions in the same Segment.
20
20
  Usually, the goal is use the information as soon as possible, but it takes a finite time to propagate from the
21
- acquisition unit to the drive channels that execute the Instructions conditionally.
21
+ acquisition unit to the AWG that execute the Instructions conditionally.
22
22
 
23
23
  .. note::
24
24
 
@@ -31,7 +31,7 @@ To facilitate efficient timing of the feedback signals, IQM Pulse uses virtual c
31
31
  (the source of the signals) and drive channels (the destinations).
32
32
  Block instructions on the virtual channel represent the travel time of the signals.
33
33
 
34
- :class:`.CCPRX_Composite` is GateImplementation of the ``cc_prx`` (classically controlled PRX) that outputs two
34
+ :class:`.CCPRX_Composite` is a GateImplementation of the ``cc_prx`` (classically controlled PRX) gate that outputs two
35
35
  TimeBoxes:
36
36
  the first one to represent the travel time, and the second one with the actual :class:`.ConditionalInstruction`.
37
37
  In typical use, both should be scheduled in the same order, to ensure the Conditionalinstrucion starts when the
@@ -62,7 +62,7 @@ The equaivalent code would be
62
62
 
63
63
 
64
64
  Instructions are spaced out in time only for visual clarity. When scheduled ASAP, they would be left-aligned
65
- such that the ConditionalInstructions start right after the associated `control_delay` has passed.
65
+ such that the ConditionalInstructions start right after the associated ``control_delay`` has passed.
66
66
 
67
67
  The bottom of the image illustrates an alternative use of ``CCPRX_Composite`` to have more freedom in the timing.
68
68
  There, the optional delay TimeBox is not used for scheduling the Instructions on QB4.
@@ -85,7 +85,7 @@ complex number or a bit, corresponding to a particular qubit in a particular seg
85
85
  In the figure, one of the AWGs has been selected as the trigger master, which means it sends trigger pulses to
86
86
  start the execution on the slave devices.
87
87
  As shown in the picture, different delays caused by the travel time of signals can be compensated for by
88
- adjusting the `trigger_delay` setting of each device.
88
+ adjusting the ``trigger_delay`` setting of each device.
89
89
 
90
90
 
91
91
  .. image:: /_static/images/pulse_timing.svg
@@ -120,4 +120,4 @@ Other notes:
120
120
  * Pipeline delays are delays between the execution of a command and the pulse actually getting outputted
121
121
  from a device. This delay is caused by the hardware and cannot be changed.
122
122
  In practice, it can be thought as being part of the cable delays, and thus can be compensated with
123
- ``trigger_delay`` setting.
123
+ the ``trigger_delay`` setting.