"""Pulse class."""
from typing import Annotated, Literal, Union
import numpy as np
from pydantic import Field
from ..serialize import Model
from .envelope import Envelope, IqWaveform, Waveform
__all__ = [
"Acquisition",
"Align",
"Delay",
"Pulse",
"PulseId",
"PulseLike",
"Readout",
"VirtualZ",
]
PulseId = int
"""Unique identifier for a pulse."""
class _PulseLike(Model):
@property
def id(self) -> PulseId:
"""Instruction identifier."""
return id(self)
[docs]class Pulse(_PulseLike):
"""A pulse to be sent to the QPU.
Valid on any channel, except acquisition ones.
"""
kind: Literal["pulse"] = "pulse"
duration: float
"""Pulse duration."""
amplitude: float
"""Pulse digital amplitude (unitless).
Pulse amplitudes are normalised between -1 and 1.
"""
envelope: Envelope
"""The pulse envelope shape.
See :class:`qibolab.Envelope` for list of available shapes.
"""
relative_phase: float = 0.0
"""Relative phase of the pulse, in radians."""
[docs] def i(self, sampling_rate: float) -> Waveform:
"""Compute the envelope of the waveform i component."""
samples = int(self.duration * sampling_rate)
return self.amplitude * self.envelope.i(samples)
[docs] def q(self, sampling_rate: float) -> Waveform:
"""Compute the envelope of the waveform q component."""
samples = int(self.duration * sampling_rate)
return self.amplitude * self.envelope.q(samples)
[docs] def envelopes(self, sampling_rate: float) -> IqWaveform:
"""Compute a tuple with the i and q envelopes."""
return np.array([self.i(sampling_rate), self.q(sampling_rate)])
[docs]class Delay(_PulseLike):
"""Wait instruction.
During its length no pulse is sent on the same channel.
Valid on any channel.
"""
kind: Literal["delay"] = "delay"
duration: float
"""Duration in ns."""
[docs]class VirtualZ(_PulseLike):
"""Implementation of Z-rotations using virtual phase.
Only valid on a drive channel.
"""
kind: Literal["virtualz"] = "virtualz"
phase: float
"""Phase that implements the rotation."""
@property
def duration(self):
"""Duration of the virtual gate should always be zero."""
return 0
[docs]class Acquisition(_PulseLike):
"""Acquisition instruction.
This event instructs the device to acquire samples for the event
span.
Only valid on an acquisition channel.
"""
kind: Literal["acquisition"] = "acquisition"
duration: float
"""Duration in ns."""
[docs]class Readout(_PulseLike):
"""Readout instruction.
This event instructs the device to acquire samples for the event
span.
Only valid on an acquisition channel.
"""
kind: Literal["readout"] = "readout"
acquisition: Acquisition
probe: Pulse
[docs] @classmethod
def from_probe(cls, probe: Pulse):
"""Create a whole readout operation from its probe pulse.
The acquisition is made to match the same probe duration.
"""
return cls(acquisition=Acquisition(duration=probe.duration), probe=probe)
@property
def duration(self) -> float:
"""Duration in ns."""
return self.acquisition.duration
@property
def id(self) -> int:
"""Instruction identifier."""
return self.acquisition.id
[docs]class Align(_PulseLike):
"""Brings different channels at the same point in time."""
kind: Literal["align"] = "align"
PulseLike = Annotated[
Union[Align, Pulse, Delay, VirtualZ, Acquisition, Readout],
Field(discriminator="kind"),
]