iqm-pulse 9.21.0__tar.gz → 10.1.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.
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/CHANGELOG.rst +27 -2
- {iqm_pulse-9.21.0/src/iqm_pulse.egg-info → iqm_pulse-10.1.0}/PKG-INFO +1 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/API.rst +1 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/concepts.rst +27 -23
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/conf.py +1 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/custom_gates.rst +72 -68
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/pulse_timing.rst +10 -10
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/using_builder.rst +29 -23
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/builder.py +188 -89
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/circuit_operations.py +0 -5
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gate_implementation.py +242 -100
- iqm_pulse-10.1.0/src/iqm/pulse/gates/__init__.py +240 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/conditional.py +3 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/cz.py +7 -15
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/default_gates.py +17 -117
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/flux_multiplexer.py +2 -2
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/measure.py +46 -41
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/prx.py +8 -5
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/reset.py +8 -16
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/rz.py +1 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/sx.py +1 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/u.py +1 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/instructions.py +7 -7
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/waveforms.py +29 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/quantum_ops.py +29 -31
- iqm_pulse-10.1.0/src/iqm/pulse/utils.py +109 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0/src/iqm_pulse.egg-info}/PKG-INFO +1 -1
- iqm_pulse-10.1.0/version.txt +1 -0
- iqm_pulse-9.21.0/src/iqm/pulse/gates/__init__.py +0 -356
- iqm_pulse-9.21.0/src/iqm/pulse/utils.py +0 -201
- iqm_pulse-9.21.0/version.txt +0 -1
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/LICENSE.txt +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/MANIFEST.in +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/README.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/Makefile +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/.gitignore +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/css/custom.css +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/images/favicon.ico +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/images/feedback_timing.svg +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/images/logo.png +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/images/playlist_breakdown.svg +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/images/pulse_timing.svg +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_static/images/readout_timing.svg +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_templates/autosummary-class-template.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/_templates/autosummary-module-template.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/changelog.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/index.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/license.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/references.bib +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/docs/references.rst +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/pyproject.toml +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/requirements/base.in +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/requirements/base.txt +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/setup.cfg +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/setup.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/__init__.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/base_utils.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/barrier.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/delay.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/enums.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/gates/move.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/__init__.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/channel.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/fast_drag.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/hd_drag.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/playlist.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/schedule.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/__init__.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/base.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/templates/playlist_inspection.jinja2 +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/templates/static/moment.min.js +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/py.typed +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/scheduler.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/timebox.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm/pulse/validation.py +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm_pulse.egg-info/SOURCES.txt +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm_pulse.egg-info/dependency_links.txt +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm_pulse.egg-info/requires.txt +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/src/iqm_pulse.egg-info/top_level.txt +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/tests/.pylintrc +0 -0
- {iqm_pulse-9.21.0 → iqm_pulse-10.1.0}/tests/__init__.py +0 -0
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 10.1.0 (2025-07-18)
|
|
6
|
+
===========================
|
|
7
|
+
|
|
8
|
+
Features
|
|
9
|
+
--------
|
|
10
|
+
- Add a new "prx" implementation :class:`PRX_Cosine`, using a cosine pulse for both I and Q components.
|
|
11
|
+
- Add a new waveform :class:`PolynomialCosine`, which is a polynomial function of a cosine,
|
|
12
|
+
:math:`f(n) = \sum_{k=0}^m a_k \cos^k(2 \pi f n)`, where :math:`n` is the sample index.
|
|
13
|
+
|
|
14
|
+
Bug Fixes
|
|
15
|
+
---------
|
|
16
|
+
- In the waveform :class:`Cosine` add ``phase`` to :attr:`non_timelike_attributes`.
|
|
17
|
+
|
|
18
|
+
Version 10.0.0 (2025-07-16)
|
|
19
|
+
===========================
|
|
20
|
+
|
|
21
|
+
Breaking changes
|
|
22
|
+
----------------
|
|
23
|
+
|
|
24
|
+
- :class:`.CompositeGate` subclasses must now include all their member gates in :attr:`.CompositeGate.registered_gates`.
|
|
25
|
+
The subclasses should apply the member gates using :meth:`.CompositeGate.build`.
|
|
26
|
+
- Removed the :meth:`.PRX_SinglePulse_GateImplementation.iq_pulse` alias, use ``.pulse`` instead.
|
|
27
|
+
- :func:`.register_implementation` no longer can register or define a QuantumOp.
|
|
28
|
+
:func:`.register_operation` is introduced for that purpose.
|
|
29
|
+
|
|
5
30
|
Version 9.21.0 (2025-07-10)
|
|
6
31
|
===========================
|
|
7
32
|
|
|
@@ -200,7 +225,7 @@ Version 8.12.0 (2025-04-03)
|
|
|
200
225
|
===========================
|
|
201
226
|
|
|
202
227
|
Feature
|
|
203
|
-
|
|
228
|
+
-------
|
|
204
229
|
|
|
205
230
|
- Format code and enable PEP 604 in linting rules, :issue:`SW-1230`.
|
|
206
231
|
|
|
@@ -217,7 +242,7 @@ Version 8.10.0 (2025-04-02)
|
|
|
217
242
|
===========================
|
|
218
243
|
|
|
219
244
|
Features
|
|
220
|
-
|
|
245
|
+
--------
|
|
221
246
|
|
|
222
247
|
- Update the documentation footer to display the package version.
|
|
223
248
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Concepts and
|
|
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 **
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
111
|
-
QuantumOps are simple, abstract, self-contained actions one can
|
|
112
|
-
|
|
113
|
-
Whereas Schedules and Instructions act on control channels, QuantumOps act on
|
|
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
|
|
117
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8
|
-
metadata to define the gate. A QuantumOp is identified by its :attr
|
|
9
|
-
:attr
|
|
10
|
-
the
|
|
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
|
|
12
|
+
of components (for example ``measure`` or ``barrier``).
|
|
13
13
|
|
|
14
|
-
The attribute :attr
|
|
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
|
|
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
|
|
27
|
-
|
|
28
|
-
:class
|
|
29
|
-
to add new implementations
|
|
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
|
|
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
|
|
41
|
-
gives the associated unitary matrix. Note that not all QuantumOps
|
|
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
|
|
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
|
|
48
|
-
to define a quantum operation and the available
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
86
|
-
|
|
87
|
-
that calibration data already at this point. Note that ScheduleBuilder caches
|
|
88
|
-
|
|
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
|
|
91
|
-
the
|
|
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
|
|
109
|
+
default_cz_box = builder.get_implementation("cz", ("QB1", "QB2"))() # CZ has no params
|
|
109
110
|
|
|
110
|
-
The base class :meth
|
|
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
|
|
113
|
-
Developers
|
|
114
|
-
|
|
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
|
|
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
|
|
144
|
-
|
|
145
|
-
given ``locus`` (see the next subsection for more info on Waveforms and calibration data). The PRX
|
|
146
|
-
``angle`` scales the pulse amplitude linearly (the waveforms are normalized to one), and the
|
|
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
|
|
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
|
|
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
|
|
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]``.
|
|
183
|
-
Each
|
|
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
|
|
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.
|
|
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
|
|
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 =
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
|
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
|
|
284
|
-
|
|
285
|
-
:class
|
|
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 :
|
|
292
|
-
|
|
293
|
-
typical circuit execution and their default implementations. These include e.g. the
|
|
294
|
-
operation, the conditional
|
|
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
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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
|
|
318
|
+
Here, the CNOT implementation ``MyCNotClass`` needs to be defined first (a QuantumOp always needs at least one
|
|
318
319
|
implementation).
|
|
319
320
|
|
|
320
|
-
|
|
321
|
-
|
|
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
|
|
10
|
-
from a simplified set of
|
|
11
|
-
The figure below shows how
|
|
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
|
|
19
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|