"""Wrapper for qibolab and laboneq pulses and sweeps."""
from typing import Optional
import laboneq.simple as lo
import numpy as np
from laboneq.dsl.experiment.pulse_library import (
sampled_pulse_complex,
sampled_pulse_real,
)
from qibolab.pulses import Drag, Gaussian, GaussianSquare, Pulse, PulseType, Rectangular
from qibolab.sweeper import Parameter
from .util import NANO_TO_SECONDS, SAMPLING_RATE
[docs]def select_pulse(pulse: Pulse):
"""Return laboneq pulse object corresponding to the given qibolab pulse."""
if isinstance(pulse.shape, Rectangular):
can_compress = pulse.type is not PulseType.READOUT
return lo.pulse_library.const(
length=round(pulse.duration * NANO_TO_SECONDS, 9),
amplitude=pulse.amplitude,
can_compress=can_compress,
)
if isinstance(pulse.shape, Gaussian):
sigma = pulse.shape.rel_sigma
return lo.pulse_library.gaussian(
length=round(pulse.duration * NANO_TO_SECONDS, 9),
amplitude=pulse.amplitude,
sigma=2 / sigma,
zero_boundaries=False,
)
if isinstance(pulse.shape, GaussianSquare):
sigma = pulse.shape.rel_sigma
width = pulse.shape.width
can_compress = pulse.type is not PulseType.READOUT
return lo.pulse_library.gaussian_square(
length=round(pulse.duration * NANO_TO_SECONDS, 9),
width=round(pulse.duration * NANO_TO_SECONDS, 9) * width,
amplitude=pulse.amplitude,
can_compress=can_compress,
sigma=2 / sigma,
zero_boundaries=False,
)
if isinstance(pulse.shape, Drag):
sigma = pulse.shape.rel_sigma
beta = pulse.shape.beta
return lo.pulse_library.drag(
length=round(pulse.duration * NANO_TO_SECONDS, 9),
amplitude=pulse.amplitude,
sigma=2 / sigma,
beta=beta,
zero_boundaries=False,
)
if np.all(pulse.envelope_waveform_q(SAMPLING_RATE).data == 0):
return sampled_pulse_real(
samples=pulse.envelope_waveform_i(SAMPLING_RATE).data,
can_compress=True,
)
else:
return sampled_pulse_complex(
samples=pulse.envelope_waveform_i(SAMPLING_RATE).data
+ (1j * pulse.envelope_waveform_q(SAMPLING_RATE).data),
can_compress=True,
)
[docs]class ZhPulse:
"""Wrapper data type that holds a qibolab pulse, the corresponding laboneq
pulse object, and any sweeps associated with this pulse."""
def __init__(self, pulse):
self.pulse: Pulse = pulse
"""Qibolab pulse."""
self.zhpulse = select_pulse(pulse)
"""Laboneq pulse."""
self.zhsweepers: list[tuple[Parameter, lo.SweepParameter]] = []
"""Parameters to be swept, along with their laboneq sweep parameter
definitions."""
self.delay_sweeper: Optional[lo.SweepParameter] = None
"""Laboneq sweep parameter if the delay of the pulse should be
swept."""
# pylint: disable=R0903
[docs] def add_sweeper(self, param: Parameter, sweeper: lo.SweepParameter):
"""Add sweeper to list of sweepers associated with this pulse."""
if param in {
Parameter.amplitude,
Parameter.frequency,
Parameter.duration,
Parameter.relative_phase,
}:
self.zhsweepers.append((param, sweeper))
elif param is Parameter.start:
if self.delay_sweeper:
raise ValueError(
"Cannot have multiple delay sweepers for a single pulse"
)
self.delay_sweeper = sweeper
else:
raise ValueError(f"Sweeping {param} is not supported")