"""Convert helper functions for rfsoc driver."""
from copy import deepcopy
from dataclasses import asdict
from functools import singledispatch
import numpy as np
import qibosoq.components.base as rfsoc
import qibosoq.components.pulses as rfsoc_pulses
from qibolab.pulses import Pulse, PulseSequence, PulseShape
from qibolab.qubits import Qubit
from qibolab.sweeper import BIAS, DURATION, START, Parameter, Sweeper
HZ_TO_MHZ = 1e-6
NS_TO_US = 1e-3
[docs]def replace_pulse_shape(
rfsoc_pulse: rfsoc_pulses.Pulse, shape: PulseShape, sampling_rate: float
) -> rfsoc_pulses.Pulse:
"""Set pulse shape parameters in rfsoc_pulses pulse object."""
if shape.name not in {"Gaussian", "Drag", "Rectangular", "Exponential"}:
new_pulse = rfsoc_pulses.Arbitrary(
**asdict(rfsoc_pulse),
i_values=shape.envelope_waveform_i(sampling_rate),
q_values=shape.envelope_waveform_q(sampling_rate),
)
return new_pulse
new_pulse_cls = getattr(rfsoc_pulses, shape.name)
if shape.name == "Rectangular":
return new_pulse_cls(**asdict(rfsoc_pulse))
if shape.name == "Gaussian":
return new_pulse_cls(**asdict(rfsoc_pulse), rel_sigma=shape.rel_sigma)
if shape.name == "Drag":
return new_pulse_cls(
**asdict(rfsoc_pulse), rel_sigma=shape.rel_sigma, beta=shape.beta
)
if shape.name == "Exponential":
return new_pulse_cls(
**asdict(rfsoc_pulse), tau=shape.tau, upsilon=shape.upsilon, weight=shape.g
)
[docs]def pulse_lo_frequency(pulse: Pulse, qubits: dict[int, Qubit]) -> int:
"""Return local_oscillator frequency (HZ) of a pulse."""
pulse_type = pulse.type.name.lower()
try:
lo_frequency = getattr(
qubits[pulse.qubit], pulse_type
).local_oscillator.frequency
except AttributeError:
lo_frequency = 0
return lo_frequency
[docs]def convert_units_sweeper(
sweeper: rfsoc.Sweeper, sequence: PulseSequence, qubits: dict[int, Qubit]
) -> rfsoc.Sweeper:
"""Convert units for `qibosoq.abstract.Sweeper` considering also LOs."""
sweeper = deepcopy(sweeper)
for idx, jdx in enumerate(sweeper.indexes):
parameter = sweeper.parameters[idx]
if parameter is rfsoc.Parameter.FREQUENCY:
pulse = sequence[jdx]
lo_frequency = pulse_lo_frequency(pulse, qubits)
sweeper.starts[idx] = (sweeper.starts[idx] - lo_frequency) * HZ_TO_MHZ
sweeper.stops[idx] = (sweeper.stops[idx] - lo_frequency) * HZ_TO_MHZ
elif parameter is rfsoc.Parameter.DELAY:
sweeper.starts[idx] *= NS_TO_US
sweeper.stops[idx] *= NS_TO_US
elif parameter is rfsoc.Parameter.RELATIVE_PHASE:
sweeper.starts[idx] = np.degrees(sweeper.starts[idx])
sweeper.stops[idx] = np.degrees(sweeper.stops[idx])
return sweeper
[docs]@singledispatch
def convert(*args):
"""Convert from qibolab obj to qibosoq obj, overloaded."""
raise ValueError(f"Convert function received bad parameters ({type(args[0])}).")
@convert.register
def _(qubit: Qubit) -> rfsoc.Qubit:
"""Convert `qibolab.platforms.abstract.Qubit` to
`qibosoq.abstract.Qubit`."""
if qubit.flux:
return rfsoc.Qubit(qubit.flux.offset, qubit.flux.port.name)
return rfsoc.Qubit(0.0, None)
@convert.register
def _(
sequence: PulseSequence, qubits: dict[int, Qubit], sampling_rate: float
) -> list[rfsoc_pulses.Pulse]:
"""Convert PulseSequence to list of rfosc pulses with relative time."""
last_pulse_start = 0
list_sequence = []
for pulse in sorted(sequence.pulses, key=lambda item: item.start):
start_delay = (pulse.start - last_pulse_start) * NS_TO_US
pulse_dict = asdict(convert(pulse, qubits, start_delay, sampling_rate))
list_sequence.append(pulse_dict)
last_pulse_start = pulse.start
return list_sequence
@convert.register
def _(
pulse: Pulse, qubits: dict[int, Qubit], start_delay: float, sampling_rate: float
) -> rfsoc_pulses.Pulse:
"""Convert `qibolab.pulses.pulse` to `qibosoq.abstract.Pulse`."""
pulse_type = pulse.type.name.lower()
dac = getattr(qubits[pulse.qubit], pulse_type).port.name
adc = qubits[pulse.qubit].feedback.port.name if pulse_type == "readout" else None
lo_frequency = pulse_lo_frequency(pulse, qubits)
rfsoc_pulse = rfsoc_pulses.Pulse(
frequency=(pulse.frequency - lo_frequency) * HZ_TO_MHZ,
amplitude=pulse.amplitude,
relative_phase=np.degrees(pulse.relative_phase),
start_delay=start_delay,
duration=pulse.duration * NS_TO_US,
dac=dac,
adc=adc,
name=pulse.serial,
type=pulse_type,
)
return replace_pulse_shape(rfsoc_pulse, pulse.shape, sampling_rate)
@convert.register
def _(par: Parameter) -> rfsoc.Parameter:
"""Convert a qibolab sweeper.Parameter into a qibosoq.Parameter."""
return getattr(rfsoc.Parameter, par.name.upper())
@convert.register
def _(
sweeper: Sweeper, sequence: PulseSequence, qubits: dict[int, Qubit]
) -> rfsoc.Sweeper:
"""Convert `qibolab.sweeper.Sweeper` to `qibosoq.abstract.Sweeper`.
Note that any unit conversion is not done in this function (to avoid
to do it multiple times). Conversion will be done in
`convert_units_sweeper`.
"""
parameters = []
starts = []
stops = []
indexes = []
if sweeper.parameter is BIAS:
for qubit in sweeper.qubits:
parameters.append(rfsoc.Parameter.BIAS)
indexes.append(list(qubits.values()).index(qubit))
base_value = qubit.flux.offset
values = sweeper.get_values(base_value)
starts.append(values[0])
stops.append(values[-1])
if max(np.abs(starts)) > 1 or max(np.abs(stops)) > 1:
raise ValueError("Sweeper amplitude is set to reach values higher than 1")
else:
for pulse in sweeper.pulses:
idx_sweep = sequence.index(pulse)
indexes.append(idx_sweep)
base_value = getattr(pulse, sweeper.parameter.name)
if idx_sweep != 0 and sweeper.parameter is START:
# do the conversion from start to delay
base_value = base_value - sequence[idx_sweep - 1].start
values = sweeper.get_values(base_value)
starts.append(values[0])
stops.append(values[-1])
if sweeper.parameter is START:
parameters.append(rfsoc.Parameter.DELAY)
elif sweeper.parameter is DURATION:
parameters.append(rfsoc.Parameter.DURATION)
delta_start = values[0] - base_value
delta_stop = values[-1] - base_value
if len(sequence) > idx_sweep + 1:
# if duration-swept pulse is not last
indexes.append(idx_sweep + 1)
t_start = sequence[idx_sweep + 1].start - sequence[idx_sweep].start
parameters.append(rfsoc.Parameter.DELAY)
starts.append(t_start + delta_start)
stops.append(t_start + delta_stop)
else:
parameters.append(convert(sweeper.parameter))
return rfsoc.Sweeper(
parameters=parameters,
indexes=indexes,
starts=starts,
stops=stops,
expts=len(sweeper.values),
)