iqm-pulse 12.5.0__tar.gz → 12.6.1__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.5.0 → iqm_pulse-12.6.1}/CHANGELOG.rst +14 -0
- {iqm_pulse-12.5.0/src/iqm_pulse.egg-info → iqm_pulse-12.6.1}/PKG-INFO +1 -1
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/conditional.py +32 -13
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/waveforms.py +3 -6
- iqm_pulse-12.6.1/src/iqm/pulse/utils.py +195 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1/src/iqm_pulse.egg-info}/PKG-INFO +1 -1
- iqm_pulse-12.6.1/version.txt +1 -0
- iqm_pulse-12.5.0/src/iqm/pulse/utils.py +0 -109
- iqm_pulse-12.5.0/version.txt +0 -1
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/LICENSE.txt +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/MANIFEST.in +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/README.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/API.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/Makefile +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/.gitignore +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/css/custom.css +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/images/favicon.ico +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/images/feedback_timing.svg +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/images/logo.png +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/images/playlist_breakdown.svg +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/images/pulse_timing.svg +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_static/images/readout_timing.svg +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_templates/autosummary-class-template.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/_templates/autosummary-module-template.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/changelog.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/concepts.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/conf.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/custom_gates.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/index.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/license.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/pulse_timing.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/references.bib +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/references.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/docs/using_builder.rst +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/pyproject.toml +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/requirements/base.in +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/requirements/base.txt +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/setup.cfg +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/setup.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/__init__.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/base_utils.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/builder.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/circuit_operations.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gate_implementation.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/__init__.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/barrier.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/cz.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/default_gates.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/delay.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/enums.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/flux_multiplexer.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/measure.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/move.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/prx.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/reset.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/rz.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/sx.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/gates/u.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/__init__.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/channel.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/fast_drag.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/hd_drag.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/instructions.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/playlist.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/schedule.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/__init__.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/base.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/templates/playlist_inspection.jinja2 +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/templates/static/moment.min.js +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/py.typed +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/quantum_ops.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/scheduler.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/timebox.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm/pulse/validation.py +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm_pulse.egg-info/SOURCES.txt +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm_pulse.egg-info/dependency_links.txt +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm_pulse.egg-info/requires.txt +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/src/iqm_pulse.egg-info/top_level.txt +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/tests/.pylintrc +0 -0
- {iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/tests/__init__.py +0 -0
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 12.6.1 (2025-10-27)
|
|
6
|
+
===========================
|
|
7
|
+
|
|
8
|
+
- Bump version for 4.3.1 release. No functional changes.
|
|
9
|
+
|
|
10
|
+
Version 12.6.0 (2025-10-23)
|
|
11
|
+
===========================
|
|
12
|
+
|
|
13
|
+
Features
|
|
14
|
+
--------
|
|
15
|
+
|
|
16
|
+
- ``cc_prx`` gate now supports ``prx`` gate implementations that consists of multiple drive IQ pulses,
|
|
17
|
+
e.g. ones that use the ``sx`` gate. As a result, the ``reset`` operation now also works with them.
|
|
18
|
+
|
|
5
19
|
Version 12.5.0 (2025-10-09)
|
|
6
20
|
===========================
|
|
7
21
|
|
|
@@ -18,10 +18,10 @@ import numpy as np
|
|
|
18
18
|
from exa.common.data.parameter import CollectionType, Parameter
|
|
19
19
|
from iqm.pulse.gate_implementation import CompositeGate
|
|
20
20
|
from iqm.pulse.gates.measure import FEEDBACK_KEY
|
|
21
|
-
from iqm.pulse.
|
|
22
|
-
from iqm.pulse.playlist.instructions import Block, ConditionalInstruction, Wait
|
|
21
|
+
from iqm.pulse.playlist.instructions import Block, ConditionalInstruction, IQPulse, Wait
|
|
23
22
|
from iqm.pulse.playlist.schedule import Schedule
|
|
24
23
|
from iqm.pulse.timebox import TimeBox
|
|
24
|
+
from iqm.pulse.utils import fuse_iq_pulses
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class CCPRX_Composite(CompositeGate):
|
|
@@ -30,6 +30,12 @@ class CCPRX_Composite(CompositeGate):
|
|
|
30
30
|
Applies a PRX gate conditioned on a discriminated readout result obtained in the same segment (active feedback).
|
|
31
31
|
Applies a PRX gate if the result is 1, and a Wait of equal duration if the result is 0.
|
|
32
32
|
Uses the default implementation of PRX underneath, so no extra calibration is needed.
|
|
33
|
+
|
|
34
|
+
.. note::
|
|
35
|
+
|
|
36
|
+
Assumes that the PRX gate implementation used only consists of IQPulse instructions on the drive channel
|
|
37
|
+
of its locus qubit.
|
|
38
|
+
|
|
33
39
|
"""
|
|
34
40
|
|
|
35
41
|
registered_gates = ("prx",)
|
|
@@ -70,30 +76,43 @@ class CCPRX_Composite(CompositeGate):
|
|
|
70
76
|
|
|
71
77
|
"""
|
|
72
78
|
qubit = self.locus[0]
|
|
73
|
-
|
|
79
|
+
drive_channel = self.builder.get_drive_channel(qubit)
|
|
80
|
+
prx_gate = self.build("prx", self.locus)
|
|
81
|
+
|
|
82
|
+
# NOTE assumes that the PRX gate only has IQPulse instructions on drive_channel
|
|
83
|
+
timebox: TimeBox = prx_gate(angle, phase) # type: ignore[assignment]
|
|
84
|
+
if not timebox.atom:
|
|
85
|
+
raise RuntimeError("Received non-atomic PRX timebox.")
|
|
86
|
+
prx_instructions = timebox.atom[drive_channel]
|
|
87
|
+
iq_pulses = [inst for inst in prx_instructions if isinstance(inst, IQPulse)]
|
|
88
|
+
if len(iq_pulses) != len(prx_instructions):
|
|
89
|
+
raise RuntimeError(f"PRX drive channel has non-IQPulse instructions: {prx_instructions}")
|
|
90
|
+
|
|
91
|
+
if len(iq_pulses) > 1:
|
|
92
|
+
iq_pulse = fuse_iq_pulses(iq_pulses)
|
|
93
|
+
else:
|
|
94
|
+
iq_pulse = iq_pulses[0]
|
|
74
95
|
|
|
75
|
-
|
|
76
|
-
# FIXME assumes PRX gates only use this one implementation as the default,
|
|
77
|
-
# with just a drive channel and a single IQPulse.
|
|
78
|
-
pulse = prx_gate(angle, phase).atom[prx_gate.channel][0] # type: ignore[union-attr,index]
|
|
79
|
-
wait = Wait(pulse.duration) # idling, can be replaced with a DD sequence later on
|
|
96
|
+
wait = Wait(iq_pulse.duration) # idling, can be replaced with a DD sequence later on
|
|
80
97
|
|
|
81
98
|
# TODO: use the actual inputted label when the HW supports many labels per drive channel
|
|
82
99
|
default_label = f"{feedback_qubit}__{FEEDBACK_KEY}"
|
|
83
|
-
|
|
84
|
-
duration=
|
|
100
|
+
conditional_instruction = ConditionalInstruction(
|
|
101
|
+
duration=iq_pulse.duration,
|
|
102
|
+
condition=default_label,
|
|
103
|
+
outcomes=(wait, iq_pulse),
|
|
85
104
|
)
|
|
86
105
|
delays = self.calibration_data["control_delays"]
|
|
87
106
|
if len(delays) == 0:
|
|
88
107
|
raise ValueError(f"'control_delays' for '{self.name}' on {qubit} is empty (not calibrated).")
|
|
89
108
|
|
|
90
|
-
possible_sources = [c for c in self.builder.get_virtual_feedback_channels(qubit) if
|
|
109
|
+
possible_sources = [c for c in self.builder.get_virtual_feedback_channels(qubit) if drive_channel in c]
|
|
91
110
|
if len(delays) != len(possible_sources):
|
|
92
111
|
raise ValueError(
|
|
93
112
|
f"Not the correct amount of calibration values for 'control_delays'. Need {len(possible_sources)}"
|
|
94
113
|
f"values, got {delays}."
|
|
95
114
|
)
|
|
96
|
-
virtual_channel_name = self.builder.get_virtual_feedback_channel_for(
|
|
115
|
+
virtual_channel_name = self.builder.get_virtual_feedback_channel_for(drive_channel, feedback_qubit)
|
|
97
116
|
delay = delays[possible_sources.index(virtual_channel_name)]
|
|
98
117
|
virtual_channel = self.builder.channels[virtual_channel_name]
|
|
99
118
|
delay_samples = virtual_channel.duration_to_int_samples(
|
|
@@ -106,7 +125,7 @@ class CCPRX_Composite(CompositeGate):
|
|
|
106
125
|
)
|
|
107
126
|
delay_box.neighborhood_components = {0: {virtual_channel_name}}
|
|
108
127
|
cond = TimeBox.atomic(
|
|
109
|
-
Schedule({virtual_channel_name: [Block(0)],
|
|
128
|
+
Schedule({virtual_channel_name: [Block(0)], drive_channel: [conditional_instruction]}),
|
|
110
129
|
locus_components=[qubit],
|
|
111
130
|
label=f"Conditional PRX for {qubit}",
|
|
112
131
|
)
|
|
@@ -515,16 +515,13 @@ class ModulatedCosineRiseFall(Waveform):
|
|
|
515
515
|
|
|
516
516
|
@dataclass(frozen=True)
|
|
517
517
|
class CosineRise(Waveform):
|
|
518
|
-
r"""Cosine
|
|
518
|
+
r"""Cosine rise waveform.
|
|
519
519
|
|
|
520
|
-
This waveform assumes that during its duration, the only thing happening is signal
|
|
520
|
+
This waveform assumes that during its duration, the only thing happening is signal rising to the required
|
|
521
521
|
amplitude.
|
|
522
522
|
The waveform is made for pairing with 'Constant' waveform to enable arbitrarily long pulses with smooth rise part.
|
|
523
523
|
The rise time is equal to pulse duration.
|
|
524
524
|
|
|
525
|
-
Args:
|
|
526
|
-
rise_time: Dummy parameter, used only as due to a bug. FIXME it is not used, placed for resolving exa bug
|
|
527
|
-
|
|
528
525
|
"""
|
|
529
526
|
|
|
530
527
|
def _sample(self, sample_coords: np.ndarray) -> np.ndarray:
|
|
@@ -533,7 +530,7 @@ class CosineRise(Waveform):
|
|
|
533
530
|
|
|
534
531
|
@dataclass(frozen=True)
|
|
535
532
|
class CosineFall(Waveform):
|
|
536
|
-
r"""Cosine
|
|
533
|
+
r"""Cosine fall waveform.
|
|
537
534
|
|
|
538
535
|
This waveform assumes that during its duration, the only thing occurring is signal falling to 0.
|
|
539
536
|
The waveform is made for pairing with 'Constant' waveform to enable arbitrarily long pulses with smooth fall part.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# ********************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2024 IQM
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""Utility functions."""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from collections.abc import Iterable
|
|
21
|
+
from dataclasses import replace
|
|
22
|
+
from typing import get_args, get_origin
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
from exa.common.data.parameter import CollectionType, DataType
|
|
27
|
+
from iqm.pulse.playlist import IQPulse
|
|
28
|
+
from iqm.pulse.playlist.waveforms import Samples
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def map_waveform_param_types(type_hint: type) -> tuple[DataType, CollectionType]:
|
|
32
|
+
"""Map a python typehint into EXA Parameter's `(DataType, CollectionType)` tuple.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
type: python typehint.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A `(DataType, CollectionType)` tuple
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: for a non-supported type.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
value_error = ValueError(f"Nonsupported datatype for a waveform parameter: {type_hint}")
|
|
45
|
+
if hasattr(type_hint, "__iter__") and type_hint is not str:
|
|
46
|
+
if type_hint == np.ndarray:
|
|
47
|
+
data_type = DataType.COMPLEX # due to np.ndarray not being generic we assume complex numbers
|
|
48
|
+
collection_type = CollectionType.NDARRAY
|
|
49
|
+
return (data_type, collection_type)
|
|
50
|
+
if get_origin(type_hint) is list:
|
|
51
|
+
collection_type = CollectionType.LIST
|
|
52
|
+
type_hint = get_args(type_hint)[0]
|
|
53
|
+
else:
|
|
54
|
+
raise value_error
|
|
55
|
+
else:
|
|
56
|
+
collection_type = CollectionType.SCALAR
|
|
57
|
+
|
|
58
|
+
if type_hint is float:
|
|
59
|
+
data_type = DataType.FLOAT
|
|
60
|
+
elif type_hint is int:
|
|
61
|
+
data_type = DataType.INT
|
|
62
|
+
elif type_hint is str:
|
|
63
|
+
data_type = DataType.STRING
|
|
64
|
+
elif type_hint is complex:
|
|
65
|
+
data_type = DataType.COMPLEX
|
|
66
|
+
elif type_hint is bool:
|
|
67
|
+
data_type = DataType.BOOLEAN
|
|
68
|
+
else:
|
|
69
|
+
raise value_error
|
|
70
|
+
return (data_type, collection_type)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def normalize_angle(angle: float) -> float:
|
|
74
|
+
"""Normalize the given angle to (-pi, pi].
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
angle: angle to normalize (in radians)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
``angle`` normalized to (-pi, pi]
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
half_turn = np.pi
|
|
84
|
+
full_turn = 2 * half_turn
|
|
85
|
+
return (angle - half_turn) % -full_turn + half_turn
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def phase_transformation(psi_1: float = 0.0, psi_2: float = 0.0) -> tuple[float, float]:
|
|
89
|
+
r"""Implement an arbitrary (RZ, PRX, RZ) gate sequence by modifying the parameters of the
|
|
90
|
+
IQ pulse implementing the PRX.
|
|
91
|
+
|
|
92
|
+
By commutation rules we have
|
|
93
|
+
|
|
94
|
+
.. math::
|
|
95
|
+
RZ(\psi_2) \: PRX(\theta, \phi) \: RZ(\psi_1) = PRX(\theta, \phi+\psi_2) \: RZ(\psi_1 + \psi_2).
|
|
96
|
+
|
|
97
|
+
Hence an arbitrary (RZ, PRX, RZ) gate sequence is equivalent to (RZ, PRX) with adjusted angles.
|
|
98
|
+
|
|
99
|
+
Use case: with resonant driving, the PRX gate can be implemented using an :class:`IQPulse` instance,
|
|
100
|
+
and the preceding RZ can be handled by decrementing the local oscillator phase beforehand (something
|
|
101
|
+
the IQPulse instruction can also do), which is equivalent to rotating the local computational frame
|
|
102
|
+
around the z axis in the opposite direction of the required quantum state rotation.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
psi_1: RZ angle before the PRX (in rad)
|
|
106
|
+
psi_2: RZ angle after the PRX (in rad)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
change to the PRX phase angle (in rad),
|
|
110
|
+
phase increment for the IQ pulse that implements the remaining RZ (in rad)
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
return psi_2, -(psi_1 + psi_2)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def modulate_iq(pulse: IQPulse) -> np.ndarray:
|
|
117
|
+
"""Sampled baseband waveform of an IQ pulse.
|
|
118
|
+
|
|
119
|
+
Note that :attr:`IQPulse.phase_increment` has no effect on the sampled waveform.
|
|
120
|
+
The upconversion oscillator phase incrementation is a separate action performed by the AWG
|
|
121
|
+
that also affects future IQPulses, and thus cannot be represented by an array of waveform samples.
|
|
122
|
+
To replicate the effect of ``pulse`` on an AWG, one should first perform the increment and then
|
|
123
|
+
play the returned samples.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
pulse: IQ pulse.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
The waveform of ``pulse`` as an array of complex-valued samples.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
# TODO: Could be an IQPulse method
|
|
133
|
+
wave = pulse.wave_i.sample() * pulse.scale_i + 1j * pulse.wave_q.sample() * pulse.scale_q
|
|
134
|
+
# starting times of the samples, in units of inverse sample rate
|
|
135
|
+
wave_sampletimes = np.arange(len(wave))
|
|
136
|
+
wave *= np.exp(2j * np.pi * pulse.modulation_frequency * wave_sampletimes + 1j * pulse.phase)
|
|
137
|
+
return wave
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def fuse_iq_pulses(iq_pulses: Iterable[IQPulse]) -> IQPulse:
|
|
141
|
+
"""Fuse multiple IQPulses into one by concatenating the sampled waveforms.
|
|
142
|
+
|
|
143
|
+
Works by flushing :attr:`IQPulse.phase_increment` s to the front, updating the :attr:`IQPulse.phase` s,
|
|
144
|
+
sampling the pulses, concatenating, normalizing the amplitudes, and putting the result
|
|
145
|
+
into a new IQPulse instruction with a ``phase_increment`` that is a sum of the individual ``phase_increment`` s.
|
|
146
|
+
|
|
147
|
+
Additionally, to conserve waveform memory on the AWGs, we normalize the waveform phase by setting
|
|
148
|
+
:attr:`IQPulse.phase` of the fused pulse to the flushed phase of the first pulse.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
iq_pulses: IQPulse instructions to fuse.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Fused IQPulse that behaves indentically to the sequence ``iq_pulses`` on an AWG.
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
# flush the phase increments to the start of the pulse sequence
|
|
158
|
+
phases = np.array([i.phase for i in iq_pulses])
|
|
159
|
+
phase_increments = np.array([i.phase_increment for i in iq_pulses])
|
|
160
|
+
|
|
161
|
+
# flushed_phases[k] == phases[k] - np.sum(phase_increments[k+1:])
|
|
162
|
+
flushed_phases = phases - np.cumsum(phase_increments[::-1])[::-1] + phase_increments
|
|
163
|
+
|
|
164
|
+
# Phase normalization of the samples to save waveform memory: There is an internal degree of freedom
|
|
165
|
+
# in the sampled IQPulse: IQPulse.phase can be represented in the global phase of the samples.
|
|
166
|
+
# Fix this d.o.f. by setting the phase of the fused IQ pulse to the phase of the first constituent IQ pulse.
|
|
167
|
+
fused_phase = flushed_phases[0]
|
|
168
|
+
flushed_phases -= fused_phase
|
|
169
|
+
flushed_iq_pulses = [
|
|
170
|
+
replace(instr, phase=phase, phase_increment=0.0) for instr, phase in zip(iq_pulses, flushed_phases)
|
|
171
|
+
]
|
|
172
|
+
# sample and concatenate the IQ pulses
|
|
173
|
+
samples = np.hstack([modulate_iq(i) for i in flushed_iq_pulses])
|
|
174
|
+
|
|
175
|
+
# normalize the real and imaginary waveform components
|
|
176
|
+
def normalize(samples: np.ndarray) -> tuple[np.ndarray, float]:
|
|
177
|
+
"""Normalize real-valued samples to [-1, 1]."""
|
|
178
|
+
scale = np.max(np.abs(samples))
|
|
179
|
+
# avoid division by zero
|
|
180
|
+
if scale > 0:
|
|
181
|
+
return samples / scale, scale
|
|
182
|
+
return samples, 1.0
|
|
183
|
+
|
|
184
|
+
samples.real, scale_i = normalize(samples.real)
|
|
185
|
+
samples.imag, scale_q = normalize(samples.imag)
|
|
186
|
+
return IQPulse(
|
|
187
|
+
duration=len(samples),
|
|
188
|
+
wave_i=Samples(samples.real),
|
|
189
|
+
wave_q=Samples(samples.imag),
|
|
190
|
+
scale_i=scale_i,
|
|
191
|
+
scale_q=scale_q,
|
|
192
|
+
phase=fused_phase,
|
|
193
|
+
phase_increment=np.sum(phase_increments),
|
|
194
|
+
modulation_frequency=0.0, # modulate_iq takes care of this
|
|
195
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
12.6.1
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# ********************************************************************************
|
|
2
|
-
#
|
|
3
|
-
# Copyright 2024 IQM
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
"""Utility functions."""
|
|
17
|
-
|
|
18
|
-
from __future__ import annotations
|
|
19
|
-
|
|
20
|
-
from typing import get_args, get_origin
|
|
21
|
-
|
|
22
|
-
import numpy as np
|
|
23
|
-
|
|
24
|
-
from exa.common.data.parameter import CollectionType, DataType
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def map_waveform_param_types(type_hint: type) -> tuple[DataType, CollectionType]:
|
|
28
|
-
"""Map a python typehint into EXA Parameter's `(DataType, CollectionType)` tuple.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
type: python typehint.
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
A `(DataType, CollectionType)` tuple
|
|
35
|
-
|
|
36
|
-
Raises:
|
|
37
|
-
ValueError: for a non-supported type.
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
value_error = ValueError(f"Nonsupported datatype for a waveform parameter: {type_hint}")
|
|
41
|
-
if hasattr(type_hint, "__iter__") and type_hint is not str:
|
|
42
|
-
if type_hint == np.ndarray:
|
|
43
|
-
data_type = DataType.COMPLEX # due to np.ndarray not being generic we assume complex numbers
|
|
44
|
-
collection_type = CollectionType.NDARRAY
|
|
45
|
-
return (data_type, collection_type)
|
|
46
|
-
if get_origin(type_hint) is list:
|
|
47
|
-
collection_type = CollectionType.LIST
|
|
48
|
-
type_hint = get_args(type_hint)[0]
|
|
49
|
-
else:
|
|
50
|
-
raise value_error
|
|
51
|
-
else:
|
|
52
|
-
collection_type = CollectionType.SCALAR
|
|
53
|
-
|
|
54
|
-
if type_hint is float:
|
|
55
|
-
data_type = DataType.FLOAT
|
|
56
|
-
elif type_hint is int:
|
|
57
|
-
data_type = DataType.INT
|
|
58
|
-
elif type_hint is str:
|
|
59
|
-
data_type = DataType.STRING
|
|
60
|
-
elif type_hint is complex:
|
|
61
|
-
data_type = DataType.COMPLEX
|
|
62
|
-
elif type_hint is bool:
|
|
63
|
-
data_type = DataType.BOOLEAN
|
|
64
|
-
else:
|
|
65
|
-
raise value_error
|
|
66
|
-
return (data_type, collection_type)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def normalize_angle(angle: float) -> float:
|
|
70
|
-
"""Normalize the given angle to (-pi, pi].
|
|
71
|
-
|
|
72
|
-
Args:
|
|
73
|
-
angle: angle to normalize (in radians)
|
|
74
|
-
|
|
75
|
-
Returns:
|
|
76
|
-
``angle`` normalized to (-pi, pi]
|
|
77
|
-
|
|
78
|
-
"""
|
|
79
|
-
half_turn = np.pi
|
|
80
|
-
full_turn = 2 * half_turn
|
|
81
|
-
return (angle - half_turn) % -full_turn + half_turn
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def phase_transformation(psi_1: float = 0.0, psi_2: float = 0.0) -> tuple[float, float]:
|
|
85
|
-
r"""Implement an arbitrary (RZ, PRX, RZ) gate sequence by modifying the parameters of the
|
|
86
|
-
IQ pulse implementing the PRX.
|
|
87
|
-
|
|
88
|
-
By commutation rules we have
|
|
89
|
-
|
|
90
|
-
.. math::
|
|
91
|
-
RZ(\psi_2) \: PRX(\theta, \phi) \: RZ(\psi_1) = PRX(\theta, \phi+\psi_2) \: RZ(\psi_1 + \psi_2).
|
|
92
|
-
|
|
93
|
-
Hence an arbitrary (RZ, PRX, RZ) gate sequence is equivalent to (RZ, PRX) with adjusted angles.
|
|
94
|
-
|
|
95
|
-
Use case: with resonant driving, the PRX gate can be implemented using an :class:`IQPulse` instance,
|
|
96
|
-
and the preceding RZ can be handled by decrementing the local oscillator phase beforehand (something
|
|
97
|
-
the IQPulse instruction can also do), which is equivalent to rotating the local computational frame
|
|
98
|
-
around the z axis in the opposite direction of the required quantum state rotation.
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
psi_1: RZ angle before the PRX (in rad)
|
|
102
|
-
psi_2: RZ angle after the PRX (in rad)
|
|
103
|
-
|
|
104
|
-
Returns:
|
|
105
|
-
change to the PRX phase angle (in rad),
|
|
106
|
-
phase increment for the IQ pulse that implements the remaining RZ (in rad)
|
|
107
|
-
|
|
108
|
-
"""
|
|
109
|
-
return psi_2, -(psi_1 + psi_2)
|
iqm_pulse-12.5.0/version.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
12.5.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
|
|
File without changes
|
|
File without changes
|
{iqm_pulse-12.5.0 → iqm_pulse-12.6.1}/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
|