Source code for qibolab._core.components.configs
"""Configuration for various components.
These represent the minimal needed configuration that needs to be
exposed to users. Specific definitions of components can expose more,
and that can be used in any troubleshooting or debugging purposes by
users, but in general any user tool should try to depend only on the
configuration defined by these classes.
"""
from functools import reduce
from pathlib import Path
from typing import Annotated, Literal, Union
import numpy as np
from pydantic import Field
from ..serialize import Model, NdArray, eq
from .filters import Filter
__all__ = [
"DcConfig",
"IqConfig",
"AcquisitionConfig",
"IqMixerConfig",
"OscillatorConfig",
"Config",
"Configs",
"ChannelConfig",
"LogConfig",
]
[docs]
class Config(Model):
"""Configuration values depot."""
Configs = dict[str, Config]
"""Configuration database."""
[docs]
class DcConfig(Config):
"""Configuration for a channel that can be used to send DC pulses (i.e.
just envelopes without modulation)."""
kind: Literal["dc"] = "dc"
offset: float
"""DC offset/bias of the channel."""
filters: list[Filter] = Field(default_factory=list)
"""List of filters."""
@property
def feedback(self) -> list[float]:
feedback_coeff = [i.feedback for i in self.filters if i is not None]
return reduce(np.convolve, feedback_coeff, [1])
@property
def feedforward(self) -> list[float]:
feedforward_coeff = [i.feedforward for i in self.filters if i is not None]
if len(feedforward_coeff) == 0:
return []
return reduce(np.convolve, feedforward_coeff)
[docs]
class OscillatorConfig(Config):
"""Configuration for an oscillator."""
kind: Literal["oscillator"] = "oscillator"
frequency: float
power: float
[docs]
class IqMixerConfig(Config):
"""Configuration for IQ mixer.
Mixers usually have various imperfections, and one needs to
compensate for them. This class holds the compensation
configuration.
"""
kind: Literal["iq-mixer"] = "iq-mixer"
offset_i: float = 0.0
"""DC offset for the I component."""
offset_q: float = 0.0
"""DC offset for the Q component."""
scale_q: float = 1.0
"""The relative amplitude scale/factor of the q channel, to account for I-Q
amplitude imbalance."""
phase_q: float = 0.0
"""The phase offset of the q channel, to account for I-Q phase
imbalance."""
[docs]
class IqConfig(Config):
"""Configuration for an IQ channel."""
kind: Literal["iq"] = "iq"
frequency: float
"""The carrier frequency of the channel."""
[docs]
class AcquisitionConfig(Config):
"""Configuration for acquisition channel.
Currently, in qibolab, acquisition channels are FIXME:
"""
kind: Literal["acquisition"] = "acquisition"
delay: float
"""Delay between readout pulse start and acquisition start."""
smearing: float
"""FIXME:"""
# FIXME: this is temporary solution to deliver the information to drivers
# until we make acquisition channels first class citizens in the sequences
# so that each acquisition command carries the info with it.
threshold: float | None = None
"""Signal threshold for discriminating ground and excited states."""
iq_angle: float | None = None
"""Signal angle in the IQ-plane for disciminating ground and excited
states."""
kernel: Annotated[NdArray | None, Field(repr=False)] = None
"""Integration weights to be used when post-processing the acquired
signal."""
def __eq__(self, other) -> bool:
return eq(self, other)
[docs]
class LogConfig(Config):
"""Configuration for logging."""
kind: Literal["log"] = "log"
path: Path
ChannelConfig = Union[
DcConfig, IqMixerConfig, OscillatorConfig, IqConfig, AcquisitionConfig, LogConfig
]