iqm-pulse 12.1.0__tar.gz → 12.2.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-12.1.0 → iqm_pulse-12.2.0}/CHANGELOG.rst +11 -0
- {iqm_pulse-12.1.0/src/iqm_pulse.egg-info → iqm_pulse-12.2.0}/PKG-INFO +1 -1
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gate_implementation.py +18 -9
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/cz.py +10 -1
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/measure.py +188 -62
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/timebox.py +27 -15
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0/src/iqm_pulse.egg-info}/PKG-INFO +1 -1
- iqm_pulse-12.2.0/version.txt +1 -0
- iqm_pulse-12.1.0/version.txt +0 -1
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/LICENSE.txt +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/MANIFEST.in +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/README.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/API.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/Makefile +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/.gitignore +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/css/custom.css +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/images/favicon.ico +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/images/feedback_timing.svg +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/images/logo.png +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/images/playlist_breakdown.svg +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/images/pulse_timing.svg +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_static/images/readout_timing.svg +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_templates/autosummary-class-template.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/_templates/autosummary-module-template.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/changelog.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/concepts.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/conf.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/custom_gates.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/index.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/license.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/pulse_timing.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/references.bib +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/references.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/docs/using_builder.rst +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/pyproject.toml +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/requirements/base.in +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/requirements/base.txt +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/setup.cfg +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/setup.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/__init__.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/base_utils.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/builder.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/circuit_operations.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/__init__.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/barrier.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/conditional.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/default_gates.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/delay.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/enums.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/flux_multiplexer.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/move.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/prx.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/reset.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/rz.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/sx.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/gates/u.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/__init__.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/channel.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/fast_drag.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/hd_drag.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/instructions.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/playlist.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/schedule.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/__init__.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/base.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/templates/playlist_inspection.jinja2 +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/templates/static/moment.min.js +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/playlist/waveforms.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/py.typed +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/quantum_ops.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/scheduler.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/utils.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm/pulse/validation.py +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm_pulse.egg-info/SOURCES.txt +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm_pulse.egg-info/dependency_links.txt +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm_pulse.egg-info/requires.txt +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/src/iqm_pulse.egg-info/top_level.txt +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/tests/.pylintrc +0 -0
- {iqm_pulse-12.1.0 → iqm_pulse-12.2.0}/tests/__init__.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 12.2.0 (2025-09-17)
|
|
6
|
+
===========================
|
|
7
|
+
|
|
8
|
+
Features
|
|
9
|
+
--------
|
|
10
|
+
|
|
11
|
+
- Improvements to Shelved_Measure_CustomWaveforms
|
|
12
|
+
- Calibration parameter `second_prx_12_offset` that allows performing the last pre_12 operation during the ReadoutTrigger (defaults to 0s -- i.e. perform the pulse immediately after the ReadoutTrigger)
|
|
13
|
+
- Shelved_Measure_CustomWaveforms.probe_timebox now allows multiplexing. The results from this method are multiplexable with the ones coming from the base class
|
|
14
|
+
- Shelved_Measure_CustomWaveforms.time_trace works
|
|
15
|
+
|
|
5
16
|
Version 12.1.0 (2025-09-12)
|
|
6
17
|
===========================
|
|
7
18
|
|
|
@@ -516,6 +516,9 @@ class CustomIQWaveforms(GateImplementation):
|
|
|
516
516
|
if k not in cls.excluded_parameters
|
|
517
517
|
}
|
|
518
518
|
cls.parameters = root_parameters | {"i": parameters_i, "q": parameters_q}
|
|
519
|
+
# allow Mixins of CustomIQWaveforms and CompositeGate
|
|
520
|
+
if issubclass(cls, CompositeGate):
|
|
521
|
+
init_subclass_composite(cls)
|
|
519
522
|
|
|
520
523
|
|
|
521
524
|
def get_waveform_parameters(wave: type[Waveform], label_prefix: str = "") -> dict[str, Setting | Parameter]:
|
|
@@ -599,6 +602,20 @@ class SinglePulseGate(GateImplementation):
|
|
|
599
602
|
return self.builder.channels[self.channel].duration_to_seconds(self.pulse.duration)
|
|
600
603
|
|
|
601
604
|
|
|
605
|
+
def init_subclass_composite(gate_class: type[CompositeGate]) -> None:
|
|
606
|
+
if not gate_class.registered_gates:
|
|
607
|
+
# this would be pointless
|
|
608
|
+
raise ValueError(f"CompositeGate {gate_class.__name__} has no registered gates.")
|
|
609
|
+
# TODO we should also check that customizable_gates may_have_calibration (otherwise it's pointless
|
|
610
|
+
# to call them customizable), but we don't currently have access to their implementation classes here...
|
|
611
|
+
if gate_class.customizable_gates is None:
|
|
612
|
+
gate_class.customizable_gates = gate_class.registered_gates
|
|
613
|
+
elif not set(gate_class.customizable_gates) <= set(gate_class.registered_gates):
|
|
614
|
+
raise ValueError(
|
|
615
|
+
f"CompositeGate {gate_class.__name__}: customizable_gates must be a subset of registered_gates."
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
|
|
602
619
|
class CompositeGate(GateImplementation):
|
|
603
620
|
"""Base class for gate implementations that are defined in terms of other gate implementations.
|
|
604
621
|
|
|
@@ -649,15 +666,7 @@ class CompositeGate(GateImplementation):
|
|
|
649
666
|
"""
|
|
650
667
|
|
|
651
668
|
def __init_subclass__(cls):
|
|
652
|
-
|
|
653
|
-
# this would be pointless
|
|
654
|
-
raise ValueError(f"CompositeGate {cls.__name__} has no registered gates.")
|
|
655
|
-
# TODO we should also check that customizable_gates may_have_calibration (otherwise it's pointless
|
|
656
|
-
# to call them customizable), but we don't currently have access to their implementation classes here...
|
|
657
|
-
if cls.customizable_gates is None:
|
|
658
|
-
cls.customizable_gates = cls.registered_gates
|
|
659
|
-
elif not set(cls.customizable_gates) <= set(cls.registered_gates):
|
|
660
|
-
raise ValueError(f"CompositeGate {cls.__name__}: customizable_gates must be a subset of registered_gates.")
|
|
669
|
+
init_subclass_composite(cls)
|
|
661
670
|
|
|
662
671
|
@classmethod
|
|
663
672
|
def optional_calibration_keys(cls) -> tuple[str, ...]:
|
|
@@ -29,7 +29,14 @@ import numpy as np
|
|
|
29
29
|
|
|
30
30
|
from exa.common.data.parameter import Parameter, Setting
|
|
31
31
|
from exa.common.qcm_data.chip_topology import DEFAULT_2QB_MAPPING
|
|
32
|
-
from iqm.pulse.gate_implementation import
|
|
32
|
+
from iqm.pulse.gate_implementation import (
|
|
33
|
+
CompositeGate,
|
|
34
|
+
GateImplementation,
|
|
35
|
+
Locus,
|
|
36
|
+
OILCalibrationData,
|
|
37
|
+
get_waveform_parameters,
|
|
38
|
+
init_subclass_composite,
|
|
39
|
+
)
|
|
33
40
|
from iqm.pulse.playlist.instructions import Block, FluxPulse, Instruction, IQPulse, VirtualRZ
|
|
34
41
|
from iqm.pulse.playlist.schedule import Schedule
|
|
35
42
|
from iqm.pulse.playlist.waveforms import (
|
|
@@ -200,6 +207,8 @@ class FluxPulseGate(GateImplementation):
|
|
|
200
207
|
parameters["qubit"]["amplitude"] = Parameter("", "Qubit flux pulse amplitude", "")
|
|
201
208
|
|
|
202
209
|
cls.parameters = root_parameters | {k: v for k, v in parameters.items() if k not in cls.excluded_parameters}
|
|
210
|
+
if issubclass(cls, CompositeGate):
|
|
211
|
+
init_subclass_composite(cls)
|
|
203
212
|
|
|
204
213
|
def _call(self) -> TimeBox:
|
|
205
214
|
timebox = self.to_timebox(self._schedule)
|
|
@@ -15,7 +15,8 @@ r"""Projective measurement in the Z basis."""
|
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
-
from
|
|
18
|
+
from collections.abc import Iterable
|
|
19
|
+
from copy import copy, deepcopy
|
|
19
20
|
from dataclasses import replace
|
|
20
21
|
import functools
|
|
21
22
|
from typing import TYPE_CHECKING
|
|
@@ -26,6 +27,7 @@ from exa.common.data.parameter import CollectionType, DataType, Parameter, Setti
|
|
|
26
27
|
from iqm.pulse.gate_implementation import (
|
|
27
28
|
PROBE_LINES_LOCUS_MAPPING,
|
|
28
29
|
SINGLE_COMPONENTS_WITH_READOUT_LOCUS_MAPPING,
|
|
30
|
+
CompositeGate,
|
|
29
31
|
CustomIQWaveforms,
|
|
30
32
|
Locus,
|
|
31
33
|
OILCalibrationData,
|
|
@@ -33,6 +35,7 @@ from iqm.pulse.gate_implementation import (
|
|
|
33
35
|
from iqm.pulse.playlist.channel import ProbeChannelProperties
|
|
34
36
|
from iqm.pulse.playlist.instructions import (
|
|
35
37
|
AcquisitionMethod,
|
|
38
|
+
Block,
|
|
36
39
|
ComplexIntegration,
|
|
37
40
|
IQPulse,
|
|
38
41
|
MultiplexedIQPulse,
|
|
@@ -41,7 +44,7 @@ from iqm.pulse.playlist.instructions import (
|
|
|
41
44
|
TimeTrace,
|
|
42
45
|
)
|
|
43
46
|
from iqm.pulse.playlist.waveforms import Constant, Samples
|
|
44
|
-
from iqm.pulse.timebox import MultiplexedProbeTimeBox, TimeBox
|
|
47
|
+
from iqm.pulse.timebox import MultiplexedProbeTimeBox, SchedulingStrategy, TimeBox
|
|
45
48
|
|
|
46
49
|
if TYPE_CHECKING: # pragma: no cover
|
|
47
50
|
from iqm.pulse.builder import ScheduleBuilder
|
|
@@ -101,10 +104,13 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
|
|
|
101
104
|
):
|
|
102
105
|
super().__init__(parent, name, locus, calibration_data, builder)
|
|
103
106
|
|
|
104
|
-
self._multiplexed_timeboxes: dict[tuple[str, str, bool],
|
|
107
|
+
self._multiplexed_timeboxes: dict[tuple[str, str, bool], TimeBox] = {}
|
|
105
108
|
"""Cache for :meth:`probe_timebox`."""
|
|
106
109
|
self._time_traces: dict[tuple[str, float | None, float | None, str], TimeBox] = {}
|
|
107
110
|
"""Cache for :meth:`time_trace`."""
|
|
111
|
+
self._neighborhood_components: set[str] = set(self.locus) | set(
|
|
112
|
+
self.builder.chip_topology.component_to_probe_line[q] for q in self.locus
|
|
113
|
+
)
|
|
108
114
|
|
|
109
115
|
if len(locus) == 1:
|
|
110
116
|
# prepare the single-component measurement
|
|
@@ -220,9 +226,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
|
|
|
220
226
|
|
|
221
227
|
return probe_pulse, acquisition_method
|
|
222
228
|
|
|
223
|
-
def probe_timebox(
|
|
224
|
-
self, key: str = "", feedback_key: str = "", do_acquisition: bool = True, **kwargs
|
|
225
|
-
) -> MultiplexedProbeTimeBox:
|
|
229
|
+
def probe_timebox(self, key: str = "", feedback_key: str = "", do_acquisition: bool = True, **kwargs) -> TimeBox:
|
|
226
230
|
"""Returns a "naked" probe timebox that supports convenient multiplexing through
|
|
227
231
|
``MultiplexedProbeTimeBox.__add__``.
|
|
228
232
|
|
|
@@ -292,18 +296,12 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
|
|
|
292
296
|
label=f"{self.__class__.__name__} on {self.locus}",
|
|
293
297
|
)
|
|
294
298
|
else:
|
|
295
|
-
# factorizability: use the sub-implementations
|
|
296
|
-
# _skip_override used for child classes build on `Measure_CustomWaveforms` to not call `.probe_timebox`
|
|
297
|
-
# from the parent class, but from this class instead.
|
|
298
|
-
# TODO remove, make probe_timebox private or something.
|
|
299
299
|
probe_timeboxes = [
|
|
300
|
-
self.sub_implementations[c].probe_timebox(key, feedback_key, do_acquisition
|
|
300
|
+
self.sub_implementations[c].probe_timebox(key, feedback_key, do_acquisition) # type: ignore[attr-defined]
|
|
301
301
|
for c in self.locus
|
|
302
302
|
]
|
|
303
303
|
probe_timebox = functools.reduce(lambda x, y: x + y, probe_timeboxes)
|
|
304
|
-
probe_timebox.neighborhood_components[0] =
|
|
305
|
-
self.locus + (self.builder.chip_topology.component_to_probe_line[self.locus[0]],)
|
|
306
|
-
)
|
|
304
|
+
probe_timebox.neighborhood_components[0] = copy(self._neighborhood_components)
|
|
307
305
|
if feedback_key:
|
|
308
306
|
# Block all the virtual channels from the probes involved in self.locus as we cannot know what AWG
|
|
309
307
|
# might be listening to the sent bits. NOTE: No Waits are added, the channels are just blocked in
|
|
@@ -352,6 +350,11 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
|
|
|
352
350
|
final_box.neighborhood_components[0] = final_box.children[0].neighborhood_components[0]
|
|
353
351
|
return final_box
|
|
354
352
|
|
|
353
|
+
def _get_probe_timebox_for_time_trace(self, key: str = "", feedback_key: str = "") -> TimeBox:
|
|
354
|
+
"""Utility method that can be overridden in subclasses if they have a return type `.probe_pulse`."""
|
|
355
|
+
# FIXME: not needed once we align the return types of all these measure gates
|
|
356
|
+
return self.probe_timebox(key=key, feedback_key=feedback_key)
|
|
357
|
+
|
|
355
358
|
def time_trace(
|
|
356
359
|
self,
|
|
357
360
|
key: str = "",
|
|
@@ -385,12 +388,16 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
|
|
|
385
388
|
args = (key, acquisition_delay, acquisition_duration, feedback_key)
|
|
386
389
|
# additional caching for time traces since the acquisitions differ from the ones in _call
|
|
387
390
|
if args not in self._time_traces:
|
|
388
|
-
probe_timebox = deepcopy(self.
|
|
391
|
+
probe_timebox = deepcopy(self._get_probe_timebox_for_time_trace(key, feedback_key))
|
|
389
392
|
for probe_channel, segment in probe_timebox.atom.items(): # type: ignore[union-attr]
|
|
390
|
-
readout_trigger =
|
|
393
|
+
readout_trigger = None
|
|
394
|
+
for inst in segment:
|
|
395
|
+
if isinstance(inst, ReadoutTrigger):
|
|
396
|
+
readout_trigger = inst
|
|
397
|
+
break
|
|
391
398
|
# TODO instead of editing the probe_timebox output contents, we should make the function itself do this
|
|
392
399
|
# so we would not need to blindly search through the channels
|
|
393
|
-
if
|
|
400
|
+
if readout_trigger is None:
|
|
394
401
|
continue
|
|
395
402
|
|
|
396
403
|
probe_line = self.builder.channels[probe_channel]
|
|
@@ -807,62 +814,181 @@ class Probe_Constant(ProbePulse_CustomWaveforms_noIntegration, wave_i=Constant,
|
|
|
807
814
|
"""
|
|
808
815
|
|
|
809
816
|
|
|
810
|
-
class
|
|
817
|
+
class ShelvedMeasureTimeBox(TimeBox):
|
|
818
|
+
"""TimeBox representing a shelved measurement (ReadoutTrigger sandwiched between two PRX_12 operations).
|
|
819
|
+
|
|
820
|
+
ShelvedMeasureTimeBox is a composite TimeBox containing two children:
|
|
821
|
+
* first one being the first PRX_12 operation for the locus components of the measure
|
|
822
|
+
* second one being the ReadoutTrigger (MultiplexedProbeTimeBox) that includes the second PRX_12 operation.
|
|
823
|
+
|
|
824
|
+
Multiplexing is achieved so that ShelvedMeasureTimeBoxes support ``__add__`` and ``__radd__`` operations with other
|
|
825
|
+
boxes of the same type and MultiplexedProbeTimeBoxes. The multiplexing operation is defined such that the
|
|
826
|
+
initial PRX_12 boxes are added together (in case one of the multiplexed boxes is a MultiplexedProbeTimeBoxes, the
|
|
827
|
+
initial PRX_12 is considered empty), and the probe boxes are multiplexed together via the logic defined in
|
|
828
|
+
``MultiplexedProbeTimeBoxes.__add__``. This behaviour results in the correct timings of the associated pulses
|
|
829
|
+
after the multiplexing.
|
|
830
|
+
|
|
831
|
+
"""
|
|
832
|
+
|
|
833
|
+
def __post_init__(self):
|
|
834
|
+
if len(self.children) != 2:
|
|
835
|
+
raise ValueError(
|
|
836
|
+
"ShelvedMeasureTimeBox must have exactly two children: the first one corresponding to the "
|
|
837
|
+
"initial prx_12 operations, and the second one to the ReadoutTrigger and the final prx_12"
|
|
838
|
+
)
|
|
839
|
+
if self.children[1].atom is None or not isinstance(self.children[1], MultiplexedProbeTimeBox):
|
|
840
|
+
raise ValueError("The second child must be an atomic MultiplexedProbeTimeBox.")
|
|
841
|
+
|
|
842
|
+
@property
|
|
843
|
+
def prx_12_box(self) -> TimeBox:
|
|
844
|
+
return self.children[0]
|
|
845
|
+
|
|
846
|
+
@property
|
|
847
|
+
def trigger_box(self) -> TimeBox:
|
|
848
|
+
return self.children[1]
|
|
849
|
+
|
|
850
|
+
def __add__(self, other: TimeBox | Iterable[TimeBox]) -> TimeBox:
|
|
851
|
+
"""Add the initial PRX_12 boxes together via the ``TimeBox``"""
|
|
852
|
+
if isinstance(other, (ShelvedMeasureTimeBox, MultiplexedProbeTimeBox)):
|
|
853
|
+
if isinstance(other, ShelvedMeasureTimeBox):
|
|
854
|
+
prx_12_box = self.prx_12_box + other.prx_12_box
|
|
855
|
+
trigger_box = self.trigger_box + other.trigger_box
|
|
856
|
+
else:
|
|
857
|
+
prx_12_box = self.prx_12_box
|
|
858
|
+
trigger_box = self.trigger_box + other
|
|
859
|
+
locus_components = self.locus_components.union(other.locus_components)
|
|
860
|
+
multiplexed = ShelvedMeasureTimeBox(
|
|
861
|
+
label=f"Shelved measure on {locus_components}",
|
|
862
|
+
locus_components=locus_components,
|
|
863
|
+
atom=None,
|
|
864
|
+
children=(prx_12_box, trigger_box),
|
|
865
|
+
scheduling=self.scheduling,
|
|
866
|
+
scheduling_algorithm=self.scheduling_algorithm,
|
|
867
|
+
)
|
|
868
|
+
# neighborhood components by the trigger_box
|
|
869
|
+
multiplexed.neighborhood_components[0] = trigger_box.neighborhood_components[0]
|
|
870
|
+
return multiplexed
|
|
871
|
+
return super().__add__(other)
|
|
872
|
+
|
|
873
|
+
def __radd__(self, other: TimeBox | Iterable[TimeBox]) -> TimeBox:
|
|
874
|
+
return self.__add__(other)
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
SHELVED_OFFSET_TOLERANCE = 1e-12
|
|
878
|
+
"""Tolerance for the absolute value of shelved measure ``second_prx_12_offset`` calibration value
|
|
879
|
+
being considered zero."""
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
class Shelved_Measure_CustomWaveforms(Measure_CustomWaveforms, CompositeGate):
|
|
811
883
|
"""Base class for shelved readout.
|
|
812
884
|
|
|
813
885
|
Shelved readout applies a ``prx_12(pi)`` gate before and after a standard dispersive readout on each qubit measured.
|
|
814
886
|
The first ``prx_12(pi)`` swaps the amplitudes of the |1> and |2> states, and the second one swaps them back after
|
|
815
|
-
the measurement has (
|
|
887
|
+
the measurement has (roughly) collapsed the state. If the discriminator of the readout is calibrated such that
|
|
816
888
|
the |0> state is on one side and the |1> and |2> states are on the other, the end result is equivalent to the
|
|
817
889
|
standard readout operation but with the advantage that the population in the |2> state is less susceptible to
|
|
818
890
|
:math:`T_1` decay during the readout than the population in the |1> state.
|
|
819
891
|
|
|
820
|
-
.. note:: Mixed implementation multiplexing is not supported.
|
|
821
892
|
"""
|
|
822
893
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
# function explicitly. However, the main functionality of this method will not work: Enabling mixed
|
|
842
|
-
# implementation multiplexing. This is because the method has to return time boxes due to the `prx_12` pulses,
|
|
843
|
-
# instead of `MultiplexedProbeTimeBox`
|
|
844
|
-
# TODO: Enable mixed implementation multiplexing for shelved readout
|
|
845
|
-
def probe_timebox( # type: ignore[override]
|
|
846
|
-
self, key: str = "", feedback_key: str = "", do_acquisition: bool = True, _skip_override: bool = False
|
|
847
|
-
) -> TimeBox:
|
|
848
|
-
if _skip_override:
|
|
849
|
-
return super().probe_timebox(key, feedback_key, do_acquisition)
|
|
850
|
-
multiplexed_timeboxes = super().probe_timebox(key, feedback_key)
|
|
851
|
-
prx_12_impl = [self.builder.get_implementation("prx_12", [q])(np.pi) for q in self.locus]
|
|
852
|
-
|
|
853
|
-
boxes = prx_12_impl + multiplexed_timeboxes + prx_12_impl # type: ignore[operator]
|
|
854
|
-
return boxes
|
|
855
|
-
|
|
856
|
-
def _call(self, key: str = "", feedback_key: str = "") -> TimeBox: # type: ignore[override]
|
|
857
|
-
shelved_measure_box = TimeBox.composite(
|
|
858
|
-
self.probe_timebox(key=key, feedback_key=feedback_key), # type: ignore[arg-type]
|
|
859
|
-
label=f"Readout on {self.locus}",
|
|
860
|
-
)
|
|
861
|
-
shelved_measure_box.neighborhood_components[0] = shelved_measure_box.children[
|
|
862
|
-
len(self.locus)
|
|
863
|
-
].neighborhood_components[0]
|
|
894
|
+
root_parameters = Measure_CustomWaveforms.root_parameters | {
|
|
895
|
+
"second_prx_12_offset": Setting(
|
|
896
|
+
Parameter(
|
|
897
|
+
"second_prx_12_offset", "Offset of the second PRX_12 pulse from the end the ReadoutTrigger", unit="s"
|
|
898
|
+
),
|
|
899
|
+
0.0,
|
|
900
|
+
),
|
|
901
|
+
"do_prx_12": Setting(
|
|
902
|
+
Parameter(
|
|
903
|
+
"do_prx_12",
|
|
904
|
+
"Whether to do the prx_12 flips in the measure operation",
|
|
905
|
+
unit="",
|
|
906
|
+
data_type=DataType.BOOLEAN,
|
|
907
|
+
),
|
|
908
|
+
True,
|
|
909
|
+
),
|
|
910
|
+
}
|
|
911
|
+
registered_gates = ("prx_12",)
|
|
864
912
|
|
|
865
|
-
|
|
913
|
+
def probe_timebox(self, key: str = "", feedback_key: str = "", do_acquisition: bool = True, **kwargs) -> TimeBox: # type: ignore[union-attr, override]
|
|
914
|
+
"""Returns a "naked" probe timebox that supports convenient multiplexing through
|
|
915
|
+
``ShelvedMeasureTimeBox.__add__``.
|
|
916
|
+
|
|
917
|
+
This method can be used if the user wants to control the multiplexing explicitly. Supports adding together
|
|
918
|
+
boxes of type :class:`.ShelvedMeasureTimeBox` and/or :class:`.MultiplexedProbeTimeBox`. See
|
|
919
|
+
:meth:`.ShelvedMeasureTimeBox.__add__` for more information on the logic.
|
|
920
|
+
|
|
921
|
+
Args:
|
|
922
|
+
key: The readout results generated on this trigger will be assigned to
|
|
923
|
+
``f"{qubit}__{key}"``, where ``qubit`` goes over the component names in ``self.locus``. If empty,
|
|
924
|
+
the key `"readout.result"` will be used to maintain backwards compatibility.
|
|
925
|
+
feedback_key: The signals generated by this measure operation are routed using this key for
|
|
926
|
+
fast feedback purposes. See :meth:`__call__`.
|
|
927
|
+
do_acquisition: if False, no acquisitions are added.
|
|
928
|
+
|
|
929
|
+
Returns:
|
|
930
|
+
ShelvedMeasureTimeBox containing the ReadoutTrigger instruction.
|
|
931
|
+
|
|
932
|
+
"""
|
|
933
|
+
args = (key, feedback_key, do_acquisition)
|
|
934
|
+
if args not in self._multiplexed_timeboxes:
|
|
935
|
+
if len(self.locus) == 1:
|
|
936
|
+
probe_timebox = super().probe_timebox(key, feedback_key, do_acquisition, **kwargs)
|
|
937
|
+
shelved_box = probe_timebox
|
|
938
|
+
prx_12_box = TimeBox.composite(
|
|
939
|
+
[self.build("prx_12", self.locus)(np.pi)], scheduling=SchedulingStrategy.ALAP
|
|
940
|
+
)
|
|
941
|
+
if self.calibration_data["do_prx_12"]:
|
|
942
|
+
shelved_box = probe_timebox + prx_12_box # type: ignore[operator, assignment, override]
|
|
943
|
+
# schedule the shelved box to get an atomic schedule
|
|
944
|
+
shelved_atom = deepcopy(self.builder.resolve_timebox(shelved_box, neighborhood=0))
|
|
945
|
+
offset = self.calibration_data["second_prx_12_offset"]
|
|
946
|
+
if self.calibration_data["do_prx_12"] and abs(offset) > SHELVED_OFFSET_TOLERANCE:
|
|
947
|
+
drive_channel_name = self.builder.get_drive_channel(self.locus[0])
|
|
948
|
+
drive_channel = self.builder.channels[drive_channel_name]
|
|
949
|
+
offset_sign = offset / abs(offset)
|
|
950
|
+
offset_in_samples = offset_sign * drive_channel.duration_to_int_samples(abs(offset))
|
|
951
|
+
trigger_block = shelved_atom[drive_channel_name][0]
|
|
952
|
+
block_with_offset = Block(trigger_block.duration + offset_in_samples)
|
|
953
|
+
shelved_atom[drive_channel_name]._instructions[0] = block_with_offset
|
|
954
|
+
trigger_box = MultiplexedProbeTimeBox(
|
|
955
|
+
label=f"{self.__class__.__name__} on {self.locus}",
|
|
956
|
+
locus_components=probe_timebox.locus_components,
|
|
957
|
+
atom=shelved_atom,
|
|
958
|
+
)
|
|
959
|
+
trigger_box.neighborhood_components[0] = probe_timebox.neighborhood_components[0]
|
|
960
|
+
pre_box = prx_12_box if self.calibration_data["do_prx_12"] else TimeBox.composite([])
|
|
961
|
+
final_box = ShelvedMeasureTimeBox(
|
|
962
|
+
label=f"Shelved Measure on {self.locus}",
|
|
963
|
+
locus_components=set(self.locus),
|
|
964
|
+
atom=None,
|
|
965
|
+
children=(pre_box, trigger_box),
|
|
966
|
+
)
|
|
967
|
+
final_box.neighborhood_components[0] = probe_timebox.neighborhood_components[0]
|
|
968
|
+
else:
|
|
969
|
+
# NOTE: the super call can be a bit misleading; it is actually calling the `self.probe_timebox` of len 1
|
|
970
|
+
# in this class inside, via the factorizable gate's sub_implementations
|
|
971
|
+
final_box = super().probe_timebox(key, feedback_key) # type: ignore[assignment]
|
|
972
|
+
self._multiplexed_timeboxes[args] = final_box
|
|
973
|
+
return self._multiplexed_timeboxes[args]
|
|
974
|
+
|
|
975
|
+
def _get_probe_timebox_for_time_trace(self, key: str = "", feedback_key: str = "") -> TimeBox:
|
|
976
|
+
"""Utility method that can be overridden in subclasses if they have a return type `.probe_pulse`.
|
|
977
|
+
|
|
978
|
+
The ``ShelvedMeasureTimeBox`` resulting from :meth:`.probe_timebox` is first scheduled to obtain an atomic
|
|
979
|
+
``MultiplexedProbeTimeBox`` which is wrapped into a TimeBox.
|
|
980
|
+
"""
|
|
981
|
+
# FIXME: not needed once we align the return types of all these measure gates
|
|
982
|
+
probe_timebox = self.probe_timebox(key=key, feedback_key=feedback_key)
|
|
983
|
+
# resolve the box to get an atomic time_box.
|
|
984
|
+
probe_schedule = self.builder.resolve_timebox(probe_timebox, neighborhood=0)
|
|
985
|
+
atomic_probe_box = MultiplexedProbeTimeBox.atomic(
|
|
986
|
+
probe_schedule,
|
|
987
|
+
label=f"Time Trace atomic probe box of {self.__class__.__name__} on {self.locus}",
|
|
988
|
+
locus_components=probe_timebox.locus_components,
|
|
989
|
+
)
|
|
990
|
+
atomic_probe_box.neighborhood_components[0] = probe_timebox.neighborhood_components[0]
|
|
991
|
+
return atomic_probe_box
|
|
866
992
|
|
|
867
993
|
|
|
868
994
|
class Shelved_Measure_Constant(Shelved_Measure_CustomWaveforms, wave_i=Constant, wave_q=Constant): # type:ignore[call-arg]
|
|
@@ -23,7 +23,7 @@ import enum
|
|
|
23
23
|
from functools import reduce
|
|
24
24
|
|
|
25
25
|
from iqm.pulse.playlist.instructions import Block, ReadoutTrigger
|
|
26
|
-
from iqm.pulse.playlist.schedule import Schedule
|
|
26
|
+
from iqm.pulse.playlist.schedule import Schedule, Segment
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class SchedulingAlgorithm(enum.Enum):
|
|
@@ -164,8 +164,8 @@ class TimeBox:
|
|
|
164
164
|
scheduling_algorithm=scheduling_algorithm,
|
|
165
165
|
)
|
|
166
166
|
|
|
167
|
-
@
|
|
168
|
-
def atomic(schedule: Schedule, *, locus_components: Iterable[str], label: str) -> TimeBox:
|
|
167
|
+
@classmethod
|
|
168
|
+
def atomic(cls, schedule: Schedule, *, locus_components: Iterable[str], label: str) -> TimeBox:
|
|
169
169
|
"""Build an atomic timebox from a schedule.
|
|
170
170
|
|
|
171
171
|
Args:
|
|
@@ -177,7 +177,7 @@ class TimeBox:
|
|
|
177
177
|
atomic timebox containing ``schedule``
|
|
178
178
|
|
|
179
179
|
"""
|
|
180
|
-
return
|
|
180
|
+
return cls(label=label, locus_components=set(locus_components), atom=schedule, children=())
|
|
181
181
|
|
|
182
182
|
def validate(self, path: tuple[str, ...] = ()) -> None:
|
|
183
183
|
"""Validate the contents of the TimeBox.
|
|
@@ -227,6 +227,9 @@ class TimeBox:
|
|
|
227
227
|
A new instance containing the children of both boxes.
|
|
228
228
|
|
|
229
229
|
"""
|
|
230
|
+
if issubclass(type(other), TimeBox) and type(other) is not TimeBox: # strict subclass
|
|
231
|
+
# allow subclasses to override __add__ such that __radd__ also works consistent with that logic
|
|
232
|
+
return other.__radd__(self) # type: ignore[union-attr]
|
|
230
233
|
if isinstance(other, TimeBox):
|
|
231
234
|
left = self.children if self.atom is None else (self,)
|
|
232
235
|
right = other.children if other.atom is None else (other,)
|
|
@@ -243,7 +246,9 @@ class TimeBox:
|
|
|
243
246
|
except TypeError as err:
|
|
244
247
|
raise TypeError(f"Cannot add a TimeBox and a {type(other)}.") from err
|
|
245
248
|
|
|
246
|
-
def __radd__(self, other: Iterable[TimeBox]) -> TimeBox:
|
|
249
|
+
def __radd__(self, other: TimeBox | Iterable[TimeBox]) -> TimeBox:
|
|
250
|
+
if isinstance(other, TimeBox):
|
|
251
|
+
return self.__add__(other)
|
|
247
252
|
it = iter(other)
|
|
248
253
|
try:
|
|
249
254
|
first = next(it)
|
|
@@ -312,21 +317,25 @@ class MultiplexedProbeTimeBox(TimeBox):
|
|
|
312
317
|
A ``MultiplexedProbeTimeBox``'s atom contains exactly one ``ReadoutTrigger`` for each probe channel.
|
|
313
318
|
"""
|
|
314
319
|
|
|
320
|
+
def _multiplex(self, other_atom: Schedule) -> dict[str, Segment]:
|
|
321
|
+
new_segments = dict(self.atom.copy().items()) # type: ignore[union-attr]
|
|
322
|
+
for channel, segment in other_atom.items():
|
|
323
|
+
if channel not in new_segments:
|
|
324
|
+
new_segments[channel] = segment
|
|
325
|
+
elif isinstance(segment[0], ReadoutTrigger) and isinstance(new_segments[channel][0], ReadoutTrigger):
|
|
326
|
+
# multiplex the readout triggers together
|
|
327
|
+
new_segments[channel]._instructions[0] = new_segments[channel][0] + segment[0]
|
|
328
|
+
else:
|
|
329
|
+
new_segments[channel].extend(iter(segment))
|
|
330
|
+
return new_segments
|
|
331
|
+
|
|
315
332
|
def __add__(self, other: TimeBox | Iterable[TimeBox]) -> TimeBox:
|
|
316
333
|
"""Override ``__add__`` for two atomic ``MultiplexedProbeTimeBox`` instances such that ``ReadoutTrigger``s
|
|
317
334
|
belonging to the same probe channel are multiplexed together. Otherwise, behaves exactly like
|
|
318
335
|
``TimeBox.__add__``, returning a normal ``TimeBox``.
|
|
319
336
|
"""
|
|
320
337
|
if isinstance(other, MultiplexedProbeTimeBox) and self.atom is not None and other.atom is not None:
|
|
321
|
-
new_segments =
|
|
322
|
-
for channel, segment in other.atom.items():
|
|
323
|
-
if channel not in new_segments:
|
|
324
|
-
new_segments[channel] = segment
|
|
325
|
-
elif isinstance(segment[0], ReadoutTrigger) and isinstance(new_segments[channel][0], ReadoutTrigger):
|
|
326
|
-
# multiplex the readout triggers together
|
|
327
|
-
new_segments[channel]._instructions[0] = new_segments[channel][0] + segment[0]
|
|
328
|
-
else:
|
|
329
|
-
new_segments[channel].extend(iter(segment))
|
|
338
|
+
new_segments = self._multiplex(other.atom)
|
|
330
339
|
locus_components = self.locus_components.union(other.locus_components)
|
|
331
340
|
max_nb = max(
|
|
332
341
|
max(self.neighborhood_components, default=-1),
|
|
@@ -341,7 +350,7 @@ class MultiplexedProbeTimeBox(TimeBox):
|
|
|
341
350
|
neighborhood_components[nb] = self.neighborhood_components[nb].union(
|
|
342
351
|
other.neighborhood_components[nb]
|
|
343
352
|
)
|
|
344
|
-
return
|
|
353
|
+
return type(self)(
|
|
345
354
|
label=f"MultiplexedProbeTimeBox on {locus_components}",
|
|
346
355
|
locus_components=locus_components,
|
|
347
356
|
atom=Schedule(new_segments),
|
|
@@ -388,3 +397,6 @@ class MultiplexedProbeTimeBox(TimeBox):
|
|
|
388
397
|
scheduling_algorithm=SchedulingAlgorithm.HARD_BOUNDARY,
|
|
389
398
|
)
|
|
390
399
|
return box
|
|
400
|
+
|
|
401
|
+
def __radd__(self, other: TimeBox | Iterable[TimeBox]) -> TimeBox:
|
|
402
|
+
return self.__add__(other)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
12.2.0
|
iqm_pulse-12.1.0/version.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
12.1.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
|
|
File without changes
|
|
File without changes
|
{iqm_pulse-12.1.0 → iqm_pulse-12.2.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
|