iqm-pulse 10.2.0__tar.gz → 10.4.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-10.2.0 → iqm_pulse-10.4.0}/CHANGELOG.rst +18 -0
- {iqm_pulse-10.2.0/src/iqm_pulse.egg-info → iqm_pulse-10.4.0}/PKG-INFO +1 -1
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/builder.py +32 -10
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/__init__.py +4 -2
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/cz.py +308 -5
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/default_gates.py +37 -23
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/move.py +2 -2
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/waveforms.py +92 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0/src/iqm_pulse.egg-info}/PKG-INFO +1 -1
- iqm_pulse-10.4.0/version.txt +1 -0
- iqm_pulse-10.2.0/version.txt +0 -1
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/LICENSE.txt +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/MANIFEST.in +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/README.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/API.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/Makefile +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/.gitignore +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/css/custom.css +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/images/favicon.ico +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/images/feedback_timing.svg +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/images/logo.png +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/images/playlist_breakdown.svg +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/images/pulse_timing.svg +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_static/images/readout_timing.svg +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_templates/autosummary-class-template.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/_templates/autosummary-module-template.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/changelog.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/concepts.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/conf.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/custom_gates.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/index.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/license.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/pulse_timing.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/references.bib +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/references.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/docs/using_builder.rst +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/pyproject.toml +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/requirements/base.in +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/requirements/base.txt +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/setup.cfg +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/setup.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/__init__.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/base_utils.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/circuit_operations.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gate_implementation.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/barrier.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/conditional.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/delay.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/enums.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/flux_multiplexer.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/measure.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/prx.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/reset.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/rz.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/sx.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/gates/u.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/__init__.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/channel.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/fast_drag.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/hd_drag.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/instructions.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/playlist.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/schedule.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/__init__.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/base.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/templates/playlist_inspection.jinja2 +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/templates/static/moment.min.js +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/py.typed +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/quantum_ops.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/scheduler.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/timebox.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/utils.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/validation.py +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm_pulse.egg-info/SOURCES.txt +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm_pulse.egg-info/dependency_links.txt +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm_pulse.egg-info/requires.txt +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm_pulse.egg-info/top_level.txt +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/tests/.pylintrc +0 -0
- {iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/tests/__init__.py +0 -0
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 10.4.0 (2025-08-12)
|
|
6
|
+
===========================
|
|
7
|
+
|
|
8
|
+
Features
|
|
9
|
+
--------
|
|
10
|
+
|
|
11
|
+
- Allow deprecating quantum operations and/or their implementations while still preserving them in the quantum operation
|
|
12
|
+
library (the names of deprecated canonical operations are still reserved).
|
|
13
|
+
|
|
14
|
+
Version 10.3.0 (2025-08-11)
|
|
15
|
+
===========================
|
|
16
|
+
|
|
17
|
+
Feature
|
|
18
|
+
-------
|
|
19
|
+
|
|
20
|
+
- Added ``FluxPulse_SmoothConstant_SmoothConstant `` which automatically splits pulses into three parts: rise, Constant, and fall.
|
|
21
|
+
- Changed Flux pulse argument ``parameter`` type to remove ignores all over the code.
|
|
22
|
+
|
|
5
23
|
Version 10.2.0 (2025-08-08)
|
|
6
24
|
===========================
|
|
7
25
|
|
|
@@ -45,7 +45,7 @@ from iqm.pulse.gate_implementation import (
|
|
|
45
45
|
OpCalibrationDataTree,
|
|
46
46
|
)
|
|
47
47
|
from iqm.pulse.gates import _validate_implementation, get_implementation_class
|
|
48
|
-
from iqm.pulse.gates.default_gates import _quantum_ops_library
|
|
48
|
+
from iqm.pulse.gates.default_gates import _deprecated_implementations, _deprecated_ops, _quantum_ops_library
|
|
49
49
|
from iqm.pulse.playlist.channel import ChannelProperties, ProbeChannelProperties
|
|
50
50
|
from iqm.pulse.playlist.instructions import (
|
|
51
51
|
AcquisitionMethod,
|
|
@@ -184,8 +184,12 @@ def validate_quantum_circuit(
|
|
|
184
184
|
def build_quantum_ops(ops: dict[str, dict[str, Any]]) -> QuantumOpTable:
|
|
185
185
|
"""Builds the table of known quantum operations.
|
|
186
186
|
|
|
187
|
-
Hardcoded
|
|
188
|
-
In case of name collisions, the content of ``ops`` takes priority over the defaults
|
|
187
|
+
Hardcoded canonical ops table is extended by the ones in ``ops``.
|
|
188
|
+
In case of name collisions, the content of ``ops`` takes priority over the defaults,
|
|
189
|
+
with the following caveats:
|
|
190
|
+
|
|
191
|
+
* canonical implementation names cannot be redefined, and
|
|
192
|
+
* for canonical operations you can only change :attr:`implementations` and :attr:`defaults_for_locus`.
|
|
189
193
|
|
|
190
194
|
Args:
|
|
191
195
|
ops: Contents of the ``gate_definitions`` section defining
|
|
@@ -204,7 +208,23 @@ def build_quantum_ops(ops: dict[str, dict[str, Any]]) -> QuantumOpTable:
|
|
|
204
208
|
ValueError: Operation attributes don't match defaults or are invalid.
|
|
205
209
|
|
|
206
210
|
"""
|
|
207
|
-
|
|
211
|
+
# build the table of native ops
|
|
212
|
+
op_table = {}
|
|
213
|
+
for op_name, op in _quantum_ops_library.items():
|
|
214
|
+
# filter out deprecated ops, unless requested by the user in ``ops``
|
|
215
|
+
if op_name in _deprecated_ops and op_name not in ops:
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
new_op = copy.deepcopy(op) # to prevent modifications to the hardcoded table
|
|
219
|
+
if (deprecated_impls := _deprecated_implementations.get(op_name)) is not None:
|
|
220
|
+
# filter out deprecated implementations
|
|
221
|
+
non_deprecated = {
|
|
222
|
+
impl_name: impl for impl_name, impl in op.implementations.items() if impl_name not in deprecated_impls
|
|
223
|
+
}
|
|
224
|
+
new_op = replace(op, implementations=non_deprecated)
|
|
225
|
+
op_table[op_name] = new_op
|
|
226
|
+
|
|
227
|
+
# add ops defined by the user
|
|
208
228
|
for op_name, op_definition in ops.items():
|
|
209
229
|
# prepare the implementations
|
|
210
230
|
implementations: dict[str, type[GateImplementation]] = {}
|
|
@@ -236,9 +256,9 @@ def build_quantum_ops(ops: dict[str, dict[str, Any]]) -> QuantumOpTable:
|
|
|
236
256
|
f"appear in the implementations dict."
|
|
237
257
|
)
|
|
238
258
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
#
|
|
259
|
+
if (old_op := _quantum_ops_library.get(op_name)) is not None:
|
|
260
|
+
# modify a canonical operation
|
|
261
|
+
# only some fields can be modified, and they have been popped out already from op_definition
|
|
242
262
|
if op_definition:
|
|
243
263
|
# TODO this should be an error, but there are so many old experiment.yml files in use
|
|
244
264
|
# that still have the old syntax that being strict about this would be disruptive.
|
|
@@ -247,15 +267,17 @@ def build_quantum_ops(ops: dict[str, dict[str, Any]]) -> QuantumOpTable:
|
|
|
247
267
|
f"'{op_name}' is a canonical operation, which means the fields {set(op_definition)} "
|
|
248
268
|
"provided by the user may not be changed."
|
|
249
269
|
)
|
|
250
|
-
|
|
270
|
+
|
|
271
|
+
op = replace(old_op, implementations=implementations, defaults_for_locus=defaults_for_locus)
|
|
251
272
|
else:
|
|
252
|
-
# new
|
|
253
|
-
|
|
273
|
+
# entirely new quantum operation defined by the user
|
|
274
|
+
op = QuantumOp(
|
|
254
275
|
name=op_name,
|
|
255
276
|
implementations=implementations,
|
|
256
277
|
defaults_for_locus=defaults_for_locus,
|
|
257
278
|
**op_definition,
|
|
258
279
|
)
|
|
280
|
+
op_table[op_name] = op
|
|
259
281
|
|
|
260
282
|
return op_table
|
|
261
283
|
|
|
@@ -42,10 +42,11 @@ from iqm.pulse.gates.cz import (
|
|
|
42
42
|
CZ_Slepian_ACStarkCRF,
|
|
43
43
|
CZ_Slepian_CRF,
|
|
44
44
|
CZ_TruncatedGaussianSmoothedSquare,
|
|
45
|
+
FluxPulse_SmoothConstant_SmoothConstant,
|
|
45
46
|
FluxPulseGate_CRF_CRF,
|
|
46
47
|
FluxPulseGate_TGSS_CRF,
|
|
47
48
|
)
|
|
48
|
-
from iqm.pulse.gates.default_gates import
|
|
49
|
+
from iqm.pulse.gates.default_gates import _implementation_library, _quantum_ops_library
|
|
49
50
|
from iqm.pulse.gates.delay import Delay
|
|
50
51
|
from iqm.pulse.gates.flux_multiplexer import FluxMultiplexer_SampleLinear
|
|
51
52
|
from iqm.pulse.gates.measure import Measure_Constant, Measure_Constant_Qnd, Shelved_Measure_Constant
|
|
@@ -98,6 +99,7 @@ _exposed_implementations: dict[str, type[GateImplementation]] = {
|
|
|
98
99
|
CZ_Slepian,
|
|
99
100
|
CZ_Slepian_CRF,
|
|
100
101
|
CZ_CRF,
|
|
102
|
+
FluxPulse_SmoothConstant_SmoothConstant,
|
|
101
103
|
CZ_TruncatedGaussianSmoothedSquare,
|
|
102
104
|
FluxPulseGate_TGSS_CRF,
|
|
103
105
|
FluxPulseGate_CRF_CRF,
|
|
@@ -155,7 +157,7 @@ def _validate_implementation(
|
|
|
155
157
|
ValueError: A canonical implementation name is being redefined.
|
|
156
158
|
|
|
157
159
|
"""
|
|
158
|
-
default_implementations =
|
|
160
|
+
default_implementations = _implementation_library.get(op_name, {})
|
|
159
161
|
|
|
160
162
|
# check if the implementation name is canonical for this op
|
|
161
163
|
if (impl_class := default_implementations.get(impl_name)) is not None:
|
|
@@ -22,6 +22,7 @@ It can be represented by the unitary matrix
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
24
|
from dataclasses import replace
|
|
25
|
+
import logging
|
|
25
26
|
from typing import TYPE_CHECKING
|
|
26
27
|
|
|
27
28
|
import numpy as np
|
|
@@ -32,14 +33,16 @@ from iqm.pulse.gate_implementation import GateImplementation, Locus, OILCalibrat
|
|
|
32
33
|
from iqm.pulse.playlist.instructions import Block, FluxPulse, Instruction, IQPulse, VirtualRZ
|
|
33
34
|
from iqm.pulse.playlist.schedule import Schedule
|
|
34
35
|
from iqm.pulse.playlist.waveforms import (
|
|
36
|
+
Constant,
|
|
37
|
+
CosineFallFlex,
|
|
35
38
|
CosineRiseFall,
|
|
39
|
+
CosineRiseFlex,
|
|
36
40
|
GaussianSmoothedSquare,
|
|
37
41
|
ModulatedCosineRiseFall,
|
|
38
42
|
Slepian,
|
|
39
43
|
TruncatedGaussianSmoothedSquare,
|
|
40
44
|
Waveform,
|
|
41
45
|
)
|
|
42
|
-
from iqm.pulse.timebox import TimeBox
|
|
43
46
|
from iqm.pulse.utils import phase_transformation
|
|
44
47
|
|
|
45
48
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -79,9 +82,9 @@ class FluxPulseGate(GateImplementation):
|
|
|
79
82
|
"""Flux pulse Waveform to be played in the coupler flux AWG."""
|
|
80
83
|
qubit_wave: type[Waveform] | None
|
|
81
84
|
"""Flux pulse Waveform to be played in the qubit flux AWG."""
|
|
82
|
-
root_parameters: dict[str, Parameter | Setting] = {
|
|
85
|
+
root_parameters: dict[str, Parameter | Setting | dict] = {
|
|
83
86
|
"duration": Parameter("", "Gate duration", "s"),
|
|
84
|
-
"rz": {
|
|
87
|
+
"rz": {
|
|
85
88
|
"*": Parameter("", "Z rotation angle", "rad"), # wildcard parameter
|
|
86
89
|
},
|
|
87
90
|
}
|
|
@@ -261,9 +264,9 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
|
|
|
261
264
|
qubit_drive_wave: type[Waveform] | None
|
|
262
265
|
"""Qubit drive pulse waveform to be played in the qubit drive AWG."""
|
|
263
266
|
|
|
264
|
-
root_parameters: dict[str, Parameter | Setting] = {
|
|
267
|
+
root_parameters: dict[str, Parameter | Setting | dict] = {
|
|
265
268
|
"duration": Parameter("", "Gate duration", "s"),
|
|
266
|
-
"rz": {
|
|
269
|
+
"rz": {
|
|
267
270
|
"*": Parameter("", "Z rotation angle", "rad"),
|
|
268
271
|
},
|
|
269
272
|
}
|
|
@@ -426,3 +429,303 @@ class CZ_CRF_ACStarkCRF(
|
|
|
426
429
|
CZ gate implemented using a cosine rise fall flux pulse for the coupler and a modulated
|
|
427
430
|
cosine rise fall (CRF) AC Stark pulse on one qubit.
|
|
428
431
|
"""
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def round_to_granularity(value: float, granularity: float, precision: float = 1e-15) -> float:
|
|
435
|
+
"""Round a value to the nearest multiple of granularity.
|
|
436
|
+
If the value is within a given precision of a multiple, round to that multiple.
|
|
437
|
+
Otherwise, round down to the nearest lower multiple.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
value:
|
|
441
|
+
granularity: granularity
|
|
442
|
+
precision: rounding precision.
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
value rounded to a granularity.
|
|
446
|
+
|
|
447
|
+
"""
|
|
448
|
+
return np.floor(value / granularity + precision) * granularity
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def split_flat_top_part_into_granular_parts(
|
|
452
|
+
duration: float, full_width: float, rise_time: float, granularity: float, precision: float = 1e-10
|
|
453
|
+
) -> tuple[float, float, float, float]:
|
|
454
|
+
"""To save waveform memory, a (long) flat-top pulse, which is defined by its duration, full_width and rise_time,
|
|
455
|
+
is divided into three consecutive parts (rise, flat, and fall),
|
|
456
|
+
all of which conform to the granularity of the device.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
duration: pulse duration in seconds.
|
|
460
|
+
full_width: full width of the pulse.
|
|
461
|
+
rise_time: rise time of the pulse.
|
|
462
|
+
granularity: minimum allowed pulse duration.
|
|
463
|
+
precision: precision of rounding to granularity,
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
A tuple containing:
|
|
468
|
+
- flat part duration
|
|
469
|
+
- rise (or fall) part duration
|
|
470
|
+
- rise time
|
|
471
|
+
- flat part's non-granular leftover, which is transferred to the rise and fall parts
|
|
472
|
+
|
|
473
|
+
Raises:
|
|
474
|
+
ValueError: Error is raised if duration is not a multiple of granularity.
|
|
475
|
+
ValueError: Error is raised if pulse parameters do not obey duration >= full_width >= 2*rise_time.
|
|
476
|
+
|
|
477
|
+
"""
|
|
478
|
+
# Check if the number of samples is within 0.005 samples of an integer number, considered safe.
|
|
479
|
+
if not round(duration / granularity, ndigits=2).is_integer():
|
|
480
|
+
raise ValueError("Duration must be a multiple of granularity.")
|
|
481
|
+
|
|
482
|
+
if (duration >= full_width) & (full_width >= 2 * rise_time):
|
|
483
|
+
plateau_width = full_width - 2 * rise_time
|
|
484
|
+
|
|
485
|
+
plateau_width_granular = round_to_granularity(plateau_width, granularity)
|
|
486
|
+
rise_duration = (duration - plateau_width_granular) / 2
|
|
487
|
+
|
|
488
|
+
if np.abs(rise_duration - np.round(rise_duration / granularity) * granularity) > precision:
|
|
489
|
+
plateau_width_granular -= granularity
|
|
490
|
+
rise_duration = (duration - plateau_width_granular) / 2
|
|
491
|
+
|
|
492
|
+
flat_part = duration - 2 * rise_duration
|
|
493
|
+
plateau_leftover = (full_width - 2 * rise_time - flat_part) / 2
|
|
494
|
+
|
|
495
|
+
return plateau_width_granular, rise_duration, rise_time, plateau_leftover
|
|
496
|
+
else:
|
|
497
|
+
raise ValueError(
|
|
498
|
+
f"Current pulse parameters (duration {duration}, full_width {full_width}, rise_time {rise_time}) "
|
|
499
|
+
f"are impossible, please use duration >= full_width >= 2*rise_time."
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
class FluxPulseGate_SmoothConstant(FluxPulseGate):
|
|
504
|
+
"""Flux pulse gate implementation realized as a 3-part pulse sequence,
|
|
505
|
+
consisting of |cosine rise|Constant|cosine fall|. Otherwise, works similar to FluxPulseGate.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
flux_pulses: mapping from flux channel name to its flux pulse
|
|
509
|
+
rz: mapping from drive channel name to the virtual z rotation angle, in radians, that should be performed on it
|
|
510
|
+
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
coupler_wave: Constant | None
|
|
514
|
+
"""Flux pulse Waveform to be played in the coupler flux AWG. Can be only Constant or None"""
|
|
515
|
+
qubit_wave: Constant | None
|
|
516
|
+
"""Flux pulse Waveform to be played in the qubit flux AWG. Can be only Constant or None"""
|
|
517
|
+
rise_wave: type[Waveform] = CosineRiseFlex
|
|
518
|
+
"""Waveform, rise part of the 3-pulse sequence to be played with qubit and coupler gates."""
|
|
519
|
+
fall_wave: type[Waveform] = CosineFallFlex
|
|
520
|
+
"""Waveform, fall part of the 3-pulse sequence to be played with qubit and coupler gates."""
|
|
521
|
+
|
|
522
|
+
root_parameters: dict[str, Parameter | Setting | dict] = {
|
|
523
|
+
"duration": Parameter("", "Gate duration", "s"),
|
|
524
|
+
"qubit": {
|
|
525
|
+
"rise_time": Parameter("", "Qubit pulse rise time", "s"),
|
|
526
|
+
"full_width": Parameter("", "Qubit pulse full width", "s"),
|
|
527
|
+
"amplitude": Parameter("", "Qubit pulse amplitude", ""),
|
|
528
|
+
},
|
|
529
|
+
"coupler": {
|
|
530
|
+
"rise_time": Parameter("", "Coupler pulse rise time", "s"),
|
|
531
|
+
"full_width": Parameter("", "Coupler pulse full width", "s"),
|
|
532
|
+
"amplitude": Parameter("", "Coupler pulse amplitude", ""),
|
|
533
|
+
},
|
|
534
|
+
"rz": {
|
|
535
|
+
"*": Parameter("", "Z rotation angle", "rad"),
|
|
536
|
+
},
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
def __init__(
|
|
540
|
+
self,
|
|
541
|
+
parent: QuantumOp,
|
|
542
|
+
name: str,
|
|
543
|
+
locus: Locus,
|
|
544
|
+
calibration_data: OILCalibrationData,
|
|
545
|
+
builder: ScheduleBuilder,
|
|
546
|
+
) -> None:
|
|
547
|
+
GateImplementation.__init__(self, parent, name, locus, calibration_data, builder)
|
|
548
|
+
duration = calibration_data["duration"]
|
|
549
|
+
|
|
550
|
+
flux_pulses = {}
|
|
551
|
+
rise_pulses = {}
|
|
552
|
+
fall_pulses = {}
|
|
553
|
+
|
|
554
|
+
def build_flux_pulse(waveform_class: type[Waveform], component_name: str, cal_node_name: str) -> None:
|
|
555
|
+
"""Uses a part of the gate calibration data to prepare a flux pulse for the given component."""
|
|
556
|
+
flux_channel = builder.get_flux_channel(component_name)
|
|
557
|
+
|
|
558
|
+
granularity = builder.channels[flux_channel].duration_to_seconds(
|
|
559
|
+
builder.channels[flux_channel].instruction_duration_min
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
data = calibration_data[cal_node_name]
|
|
563
|
+
calibration_data_constant = data.copy()
|
|
564
|
+
calibration_data_rise = data.copy()
|
|
565
|
+
|
|
566
|
+
plateau_width_granular, rise_duration, rise_time, plateau_leftover = (
|
|
567
|
+
split_flat_top_part_into_granular_parts(duration, data["full_width"], data["rise_time"], granularity)
|
|
568
|
+
)
|
|
569
|
+
calibration_data_rise["rise_time"] = rise_time
|
|
570
|
+
calibration_data_constant["duration"] = plateau_width_granular
|
|
571
|
+
calibration_data_rise["duration"] = rise_duration
|
|
572
|
+
calibration_data_rise["full_width"] = plateau_leftover + rise_time
|
|
573
|
+
|
|
574
|
+
if plateau_width_granular > 0:
|
|
575
|
+
params_for_flux_pulses = self.convert_calibration_data(
|
|
576
|
+
calibration_data=calibration_data_constant,
|
|
577
|
+
params=self.parameters[cal_node_name], # type: ignore[arg-type]
|
|
578
|
+
channel_props=builder.channels[flux_channel],
|
|
579
|
+
duration=plateau_width_granular,
|
|
580
|
+
)
|
|
581
|
+
else:
|
|
582
|
+
params_for_flux_pulses = {"n_samples": 0, "amplitude": calibration_data_constant["amplitude"]}
|
|
583
|
+
|
|
584
|
+
params_for_risefall = self.convert_calibration_data(
|
|
585
|
+
calibration_data=calibration_data_rise,
|
|
586
|
+
params=self.parameters[cal_node_name], # type: ignore[arg-type]
|
|
587
|
+
channel_props=builder.channels[flux_channel],
|
|
588
|
+
duration=rise_duration,
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
params_for_flux_pulses["n_samples"] = (
|
|
592
|
+
builder.channels[flux_channel].duration_to_int_samples(plateau_width_granular)
|
|
593
|
+
if plateau_width_granular > 0
|
|
594
|
+
else 0
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
params_for_risefall["n_samples"] = (
|
|
598
|
+
builder.channels[flux_channel].duration_to_int_samples(rise_duration) if rise_duration > 0 else 0
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
amplitude = params_for_flux_pulses.pop("amplitude")
|
|
602
|
+
params_for_risefall.pop("amplitude")
|
|
603
|
+
|
|
604
|
+
flux_pulses[flux_channel] = (
|
|
605
|
+
FluxPulse(
|
|
606
|
+
duration=params_for_flux_pulses["n_samples"],
|
|
607
|
+
wave=waveform_class(n_samples=params_for_flux_pulses["n_samples"]),
|
|
608
|
+
scale=amplitude,
|
|
609
|
+
)
|
|
610
|
+
if params_for_flux_pulses["n_samples"] > 0
|
|
611
|
+
else None
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
if params_for_risefall["n_samples"] > 0:
|
|
615
|
+
rise_pulses[flux_channel] = FluxPulse(
|
|
616
|
+
duration=params_for_risefall["n_samples"],
|
|
617
|
+
wave=self.rise_wave(**params_for_risefall),
|
|
618
|
+
scale=amplitude,
|
|
619
|
+
)
|
|
620
|
+
fall_pulses[flux_channel] = FluxPulse(
|
|
621
|
+
duration=params_for_risefall["n_samples"],
|
|
622
|
+
wave=self.fall_wave(**params_for_risefall),
|
|
623
|
+
scale=amplitude,
|
|
624
|
+
)
|
|
625
|
+
else:
|
|
626
|
+
rise_pulses[flux_channel] = None # type: ignore[assignment]
|
|
627
|
+
fall_pulses[flux_channel] = None # type: ignore[assignment]
|
|
628
|
+
|
|
629
|
+
if self.coupler_wave is not None:
|
|
630
|
+
build_flux_pulse(self.coupler_wave, builder.chip_topology.get_coupler_for(*locus), "coupler")
|
|
631
|
+
|
|
632
|
+
if self.qubit_wave is not None:
|
|
633
|
+
# the pulsed qubit is always the first one of the locus
|
|
634
|
+
build_flux_pulse(self.qubit_wave, locus[0], "qubit")
|
|
635
|
+
|
|
636
|
+
rz = calibration_data["rz"]
|
|
637
|
+
for c in locus:
|
|
638
|
+
if c not in rz:
|
|
639
|
+
raise ValueError(
|
|
640
|
+
f"{parent.name}.{name}: {locus}: Calibration is missing an RZ angle for locus component {c}."
|
|
641
|
+
)
|
|
642
|
+
rz_locus = {builder.get_drive_channel(c): angle for c, angle in rz.items() if c in locus}
|
|
643
|
+
rz_not_locus = tuple((builder.get_drive_channel(c), angle) for c, angle in rz.items() if c not in locus)
|
|
644
|
+
|
|
645
|
+
schedule: dict[str, list[Instruction]] = {
|
|
646
|
+
channel: [
|
|
647
|
+
VirtualRZ(
|
|
648
|
+
duration=builder.channels[channel].duration_to_int_samples(duration),
|
|
649
|
+
phase_increment=-angle,
|
|
650
|
+
)
|
|
651
|
+
]
|
|
652
|
+
for channel, angle in rz_locus.items()
|
|
653
|
+
}
|
|
654
|
+
vzs_inserted = False # insert the long-distance Vzs to the first flux pulse (whatever that is)
|
|
655
|
+
for channel, flux_pulse in flux_pulses.items():
|
|
656
|
+
if rz_not_locus and not vzs_inserted and flux_pulse:
|
|
657
|
+
schedule[channel] = [replace(flux_pulse, rzs=rz_not_locus)]
|
|
658
|
+
vzs_inserted = True
|
|
659
|
+
elif duration > 0:
|
|
660
|
+
schedule[channel] = [
|
|
661
|
+
v for v in [rise_pulses[channel], flux_pulse, fall_pulses[channel]] if v is not None
|
|
662
|
+
]
|
|
663
|
+
else:
|
|
664
|
+
schedule[channel] = []
|
|
665
|
+
affected_components = set(locus)
|
|
666
|
+
affected_components.add(builder.chip_topology.get_coupler_for(*locus))
|
|
667
|
+
self._affected_components = affected_components
|
|
668
|
+
self._schedule = Schedule(schedule if duration > 0 else {c: [Block(0)] for c in schedule}, duration=duration)
|
|
669
|
+
|
|
670
|
+
def __init_subclass__(
|
|
671
|
+
cls,
|
|
672
|
+
/,
|
|
673
|
+
coupler_wave: type[Waveform] | None = None,
|
|
674
|
+
qubit_wave: type[Waveform] | None = None,
|
|
675
|
+
rise_wave: type[Waveform] = CosineRiseFlex,
|
|
676
|
+
fall_wave: type[Waveform] = CosineFallFlex,
|
|
677
|
+
):
|
|
678
|
+
if coupler_wave is None and qubit_wave is None and hasattr(cls, "coupler_wave") and hasattr(cls, "qubit_wave"):
|
|
679
|
+
return
|
|
680
|
+
if coupler_wave and (coupler_wave != Constant):
|
|
681
|
+
logging.getLogger(__name__).warning(
|
|
682
|
+
"Forcing coupler wave to be Constant",
|
|
683
|
+
)
|
|
684
|
+
coupler_wave = Constant
|
|
685
|
+
if qubit_wave and (qubit_wave != Constant):
|
|
686
|
+
logging.getLogger(__name__).warning(
|
|
687
|
+
"Forcing qubit wave to be Constant",
|
|
688
|
+
)
|
|
689
|
+
qubit_wave = Constant
|
|
690
|
+
|
|
691
|
+
cls.coupler_wave = coupler_wave
|
|
692
|
+
cls.qubit_wave = qubit_wave
|
|
693
|
+
cls.symmetric = cls.qubit_wave is None
|
|
694
|
+
cls.fall_wave = fall_wave
|
|
695
|
+
cls.rise_wave = rise_wave
|
|
696
|
+
|
|
697
|
+
root_parameters = {k: v for k, v in cls.root_parameters.items() if k not in cls.excluded_parameters}
|
|
698
|
+
parameters = {}
|
|
699
|
+
if coupler_wave is not None:
|
|
700
|
+
parameters["coupler"] = (
|
|
701
|
+
get_waveform_parameters(rise_wave, label_prefix="Coupler flux pulse ")
|
|
702
|
+
| get_waveform_parameters(fall_wave, label_prefix="Coupler flux pulse ")
|
|
703
|
+
| get_waveform_parameters(coupler_wave, label_prefix="Coupler flux pulse ")
|
|
704
|
+
)
|
|
705
|
+
parameters["coupler"]["amplitude"] = Parameter("", "Coupler flux pulse amplitude", "")
|
|
706
|
+
parameters["coupler"]["rise_time"] = Parameter("", "Coupler flux pulse rise time", "s")
|
|
707
|
+
parameters["coupler"]["full_width"] = Parameter("", "Coupler flux pulse full width", "s")
|
|
708
|
+
|
|
709
|
+
if qubit_wave is not None:
|
|
710
|
+
parameters["qubit"] = (
|
|
711
|
+
get_waveform_parameters(rise_wave, label_prefix="Qubit flux pulse ")
|
|
712
|
+
| get_waveform_parameters(fall_wave, label_prefix="Qubit flux pulse ")
|
|
713
|
+
| get_waveform_parameters(qubit_wave, label_prefix="Qubit flux pulse ")
|
|
714
|
+
)
|
|
715
|
+
parameters["qubit"]["amplitude"] = Parameter("", "Qubit flux pulse amplitude", "")
|
|
716
|
+
parameters["qubit"]["rise_time"] = Parameter("", "Qubit flux pulse rise time", "s")
|
|
717
|
+
parameters["qubit"]["full_width"] = Parameter("", "Qubit flux pulse full width", "s")
|
|
718
|
+
|
|
719
|
+
cls.parameters = root_parameters | {k: v for k, v in parameters.items() if k not in cls.excluded_parameters}
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
class FluxPulse_SmoothConstant_qubit(FluxPulseGate_SmoothConstant, qubit_wave=Constant):
|
|
723
|
+
"""Constant flux pulse on qubit with smooth rise/fall"""
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
class FluxPulse_SmoothConstant_coupler(FluxPulseGate_SmoothConstant, coupler_wave=Constant):
|
|
727
|
+
"""Constant flux pulse on coupler with smooth rise/fall."""
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
class FluxPulse_SmoothConstant_SmoothConstant(FluxPulseGate_SmoothConstant, coupler_wave=Constant, qubit_wave=Constant):
|
|
731
|
+
"""Constant flux pulse on both qubit and coupler with smooth rise/fall."""
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
-
from typing import TYPE_CHECKING
|
|
18
|
+
from typing import TYPE_CHECKING, Final
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
21
|
|
|
@@ -63,7 +63,7 @@ if TYPE_CHECKING:
|
|
|
63
63
|
from iqm.pulse.gate_implementation import GateImplementation
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
_implementation_library: dict[str, dict[str, type[GateImplementation]]] = {
|
|
67
67
|
"barrier": {"": Barrier},
|
|
68
68
|
"delay": {"wait": Delay},
|
|
69
69
|
"measure": {
|
|
@@ -105,9 +105,11 @@ _default_implementations: dict[str, dict[str, type[GateImplementation]]] = {
|
|
|
105
105
|
"reset_wait": {"reset_wait": Reset_Wait},
|
|
106
106
|
"flux_multiplexer": {"sample_linear": FluxMultiplexer_SampleLinear},
|
|
107
107
|
}
|
|
108
|
-
"""
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
"""For each canonical quantum operation name, maps its canonical implementation implementation names
|
|
109
|
+
to their GateImplementation classes.
|
|
110
|
+
|
|
111
|
+
Canonical names are reserved, and the users cannot redefine them.
|
|
112
|
+
"""
|
|
111
113
|
|
|
112
114
|
_quantum_ops_library = {
|
|
113
115
|
op.name: op
|
|
@@ -115,101 +117,113 @@ _quantum_ops_library = {
|
|
|
115
117
|
QuantumOp(
|
|
116
118
|
"barrier",
|
|
117
119
|
0,
|
|
118
|
-
implementations=
|
|
120
|
+
implementations=_implementation_library["barrier"],
|
|
119
121
|
symmetric=True,
|
|
120
122
|
),
|
|
121
123
|
QuantumOp(
|
|
122
124
|
"delay",
|
|
123
125
|
0,
|
|
124
126
|
("duration",),
|
|
125
|
-
implementations=
|
|
127
|
+
implementations=_implementation_library["delay"],
|
|
126
128
|
symmetric=True,
|
|
127
129
|
),
|
|
128
130
|
QuantumOp(
|
|
129
131
|
"measure",
|
|
130
132
|
0,
|
|
131
133
|
("key",),
|
|
132
|
-
implementations=
|
|
134
|
+
implementations=_implementation_library["measure"],
|
|
133
135
|
factorizable=True,
|
|
134
136
|
),
|
|
135
137
|
QuantumOp(
|
|
136
138
|
"prx",
|
|
137
139
|
1,
|
|
138
140
|
("angle", "phase"),
|
|
139
|
-
implementations=
|
|
141
|
+
implementations=_implementation_library["prx"],
|
|
140
142
|
unitary=get_unitary_prx,
|
|
141
143
|
),
|
|
142
144
|
QuantumOp(
|
|
143
145
|
"prx_12",
|
|
144
146
|
1,
|
|
145
147
|
("angle", "phase"),
|
|
146
|
-
implementations=
|
|
148
|
+
implementations=_implementation_library["prx_12"],
|
|
147
149
|
),
|
|
148
150
|
QuantumOp(
|
|
149
151
|
"u",
|
|
150
152
|
1,
|
|
151
153
|
("theta", "phi", "lam"),
|
|
152
|
-
implementations=
|
|
154
|
+
implementations=_implementation_library["u"],
|
|
153
155
|
unitary=get_unitary_u,
|
|
154
156
|
),
|
|
155
157
|
QuantumOp(
|
|
156
158
|
"sx",
|
|
157
159
|
1,
|
|
158
|
-
implementations=
|
|
160
|
+
implementations=_implementation_library["sx"],
|
|
159
161
|
unitary=lambda: get_unitary_prx(np.pi / 2, 0),
|
|
160
162
|
),
|
|
161
163
|
QuantumOp(
|
|
162
164
|
"rz",
|
|
163
165
|
1,
|
|
164
166
|
("angle",),
|
|
165
|
-
implementations=
|
|
167
|
+
implementations=_implementation_library["rz"],
|
|
166
168
|
unitary=get_unitary_rz,
|
|
167
169
|
),
|
|
168
170
|
QuantumOp(
|
|
169
171
|
"rz_physical",
|
|
170
172
|
1,
|
|
171
|
-
implementations=
|
|
173
|
+
implementations=_implementation_library["rz_physical"],
|
|
172
174
|
),
|
|
173
175
|
QuantumOp(
|
|
174
176
|
"cz",
|
|
175
177
|
2,
|
|
176
178
|
(),
|
|
177
|
-
implementations=
|
|
179
|
+
implementations=_implementation_library["cz"],
|
|
178
180
|
symmetric=True,
|
|
179
181
|
unitary=lambda: np.diag([1.0, 1.0, 1.0, -1.0]),
|
|
180
182
|
),
|
|
181
183
|
QuantumOp(
|
|
182
184
|
"move",
|
|
183
185
|
2,
|
|
184
|
-
implementations=
|
|
186
|
+
implementations=_implementation_library["move"],
|
|
185
187
|
),
|
|
186
188
|
QuantumOp(
|
|
187
189
|
"cc_prx",
|
|
188
190
|
1,
|
|
189
191
|
("angle", "phase", "feedback_qubit", "feedback_key"),
|
|
190
|
-
implementations=
|
|
192
|
+
implementations=_implementation_library["cc_prx"],
|
|
191
193
|
),
|
|
192
194
|
QuantumOp(
|
|
193
195
|
"reset",
|
|
194
196
|
0,
|
|
195
|
-
implementations=
|
|
197
|
+
implementations=_implementation_library["reset"],
|
|
196
198
|
symmetric=True,
|
|
197
199
|
factorizable=True,
|
|
198
200
|
),
|
|
199
201
|
QuantumOp(
|
|
200
202
|
"reset_wait",
|
|
201
203
|
0,
|
|
202
|
-
implementations=
|
|
204
|
+
implementations=_implementation_library["reset_wait"],
|
|
203
205
|
symmetric=True,
|
|
204
206
|
factorizable=True,
|
|
205
207
|
),
|
|
206
208
|
QuantumOp(
|
|
207
209
|
"flux_multiplexer",
|
|
208
210
|
0,
|
|
209
|
-
implementations=
|
|
211
|
+
implementations=_implementation_library["flux_multiplexer"],
|
|
210
212
|
),
|
|
211
213
|
]
|
|
212
214
|
}
|
|
213
|
-
"""
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
"""Canonical quantum operations provided by iqm-pulse.
|
|
216
|
+
|
|
217
|
+
Their names are reserved, and the users cannot redefine them.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
_deprecated_ops: Final[set[str]] = set()
|
|
221
|
+
"""Names of canonical quantum operations that are deprecated.
|
|
222
|
+
|
|
223
|
+
They are not included by default in ScheduleBuilder unless the user specifically requests them."""
|
|
224
|
+
_deprecated_implementations: Final[dict[str, set[str]]] = {}
|
|
225
|
+
"""For each canonical quantum operation name, canonical implementation names that are deprecated.
|
|
226
|
+
|
|
227
|
+
They are not included by default in ScheduleBuilder unless the user specifically requests them."""
|
|
228
|
+
# TODO: deprecate gaussian_smoothed_square and everything with the tgss waveform as that is considered inferior to crf.
|
|
229
|
+
# TODO: deprecate PRX_drag_gaussian
|
|
@@ -102,9 +102,9 @@ class MOVE_CustomWaveforms(FluxPulseGate):
|
|
|
102
102
|
The phases are calculated and applied on the qubits using :func:`.apply_move_gate_phase_corrections`.
|
|
103
103
|
"""
|
|
104
104
|
|
|
105
|
-
root_parameters: dict[str, Parameter | Setting] = {
|
|
105
|
+
root_parameters: dict[str, Parameter | Setting | dict] = {
|
|
106
106
|
"duration": Parameter("", "Gate duration", "s"),
|
|
107
|
-
"rz": {
|
|
107
|
+
"rz": {
|
|
108
108
|
"*": Parameter("", "Z rotation angle", "rad"), # wildcard parameter
|
|
109
109
|
},
|
|
110
110
|
"detuning": Parameter("", "Qubit - resonator detuning", "Hz"),
|
|
@@ -498,3 +498,95 @@ class CosineFall(Waveform):
|
|
|
498
498
|
|
|
499
499
|
def _sample(self, sample_coords: np.ndarray) -> np.ndarray:
|
|
500
500
|
return 0.5 - 0.5 * np.sin(np.pi * sample_coords)
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
@dataclass(frozen=True)
|
|
504
|
+
class CosineRiseFlex(Waveform):
|
|
505
|
+
r"""Cosine Rise waveform with an extra duration buffer.
|
|
506
|
+
|
|
507
|
+
The waveform is a piecewise function: |buffer|cosine rise|flat plateau|, where:
|
|
508
|
+
- buffer is a 'leftover' constant signal with amplitude = 0, with duration of duration - full_width
|
|
509
|
+
- cosine rise is a cosine rise pulse with a duration of rise_time
|
|
510
|
+
- flat plateau is a constant signal with amplitude = 1, with duration of full_width - rise_time
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
rise_time: rise time of the waveform
|
|
514
|
+
full_width: combined duration of the cosine rise time and the flat plateau
|
|
515
|
+
|
|
516
|
+
Raises:
|
|
517
|
+
ValueError: Error is raised if full_width or rise_time is more than duration
|
|
518
|
+
|
|
519
|
+
"""
|
|
520
|
+
|
|
521
|
+
rise_time: float
|
|
522
|
+
full_width: float
|
|
523
|
+
|
|
524
|
+
def _sample(self, sample_coords: np.ndarray) -> np.ndarray:
|
|
525
|
+
flat_part_duration = np.abs(self.full_width) - np.abs(self.rise_time)
|
|
526
|
+
rise_time_duration = np.abs(self.rise_time)
|
|
527
|
+
dead_wait_time = 1 - np.abs(self.full_width)
|
|
528
|
+
|
|
529
|
+
if dead_wait_time >= 0:
|
|
530
|
+
return np.piecewise(
|
|
531
|
+
sample_coords,
|
|
532
|
+
[
|
|
533
|
+
sample_coords <= 0.5 - flat_part_duration - rise_time_duration,
|
|
534
|
+
sample_coords > 0.5 - flat_part_duration - rise_time_duration,
|
|
535
|
+
sample_coords >= 0.5 - flat_part_duration, # flat carry-over from the Constant
|
|
536
|
+
],
|
|
537
|
+
[
|
|
538
|
+
0,
|
|
539
|
+
lambda oc: 0.5 - 0.5 * np.cos(np.pi / rise_time_duration * (oc - dead_wait_time + 0.5)),
|
|
540
|
+
1,
|
|
541
|
+
],
|
|
542
|
+
)
|
|
543
|
+
elif (flat_part_duration + dead_wait_time > 0) and (1 - rise_time_duration >= 0):
|
|
544
|
+
raise ValueError("Full width is more than duration")
|
|
545
|
+
else:
|
|
546
|
+
raise ValueError("Rise time is more than duration")
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@dataclass(frozen=True)
|
|
550
|
+
class CosineFallFlex(Waveform):
|
|
551
|
+
r"""Cosine fall waveform with an extra duration buffer.
|
|
552
|
+
|
|
553
|
+
The waveform is a piecewise function: |flat plateau|cosine fall|buffer|, where:
|
|
554
|
+
- buffer is a 'leftover' constant signal with amplitude = 0, generally with duration of duration - full_width
|
|
555
|
+
- cosine fall is a cosine fall pulse with a duration of rise_time
|
|
556
|
+
- flat plateau is a constant signal with amplitude = 1, generally with duration of full_width - rise_time
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
rise_time: rise time of the waveform
|
|
560
|
+
full_width: combined duration of the cosine fall time and the flat plateau
|
|
561
|
+
|
|
562
|
+
Raises:
|
|
563
|
+
ValueError: Error is raised if full_width or rise_time is more than duration
|
|
564
|
+
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
rise_time: float
|
|
568
|
+
full_width: float
|
|
569
|
+
|
|
570
|
+
def _sample(self, sample_coords: np.ndarray) -> np.ndarray:
|
|
571
|
+
flat_part_duration = max(np.abs(self.full_width) - np.abs(self.rise_time), 0)
|
|
572
|
+
rise_time_duration = np.abs(self.rise_time)
|
|
573
|
+
dead_wait_time = 1 - flat_part_duration - rise_time_duration
|
|
574
|
+
|
|
575
|
+
if dead_wait_time >= 0:
|
|
576
|
+
return np.piecewise(
|
|
577
|
+
sample_coords,
|
|
578
|
+
[
|
|
579
|
+
sample_coords <= -0.5 + flat_part_duration, # flat corry-over from the Constant
|
|
580
|
+
sample_coords > -0.5 + flat_part_duration,
|
|
581
|
+
sample_coords >= -0.5 + flat_part_duration + rise_time_duration,
|
|
582
|
+
],
|
|
583
|
+
[
|
|
584
|
+
1,
|
|
585
|
+
lambda oc: 0.5 + 0.5 * np.cos(np.pi / rise_time_duration * (oc - flat_part_duration + 0.5)),
|
|
586
|
+
0,
|
|
587
|
+
],
|
|
588
|
+
)
|
|
589
|
+
elif (flat_part_duration + dead_wait_time > 0) and (1 - rise_time_duration >= 0):
|
|
590
|
+
raise ValueError("Full width is more than duration")
|
|
591
|
+
else:
|
|
592
|
+
raise ValueError("Rise time is more than duration")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
10.4.0
|
iqm_pulse-10.2.0/version.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
10.2.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iqm_pulse-10.2.0 → iqm_pulse-10.4.0}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|