Source code for atomiq.components.primitives

"""
All components in atomiq derive from the :class:`Component` class. In addition they can inherit from one ore more
primitive classes, depending on the properties of the respective component. If, for example, the component represents
a device that can measure something, it will inherit from :class:`Measurable`. If on the other hand, it can be
switched, it will inherit from :class:`Switchable`.
"""
from __future__ import annotations

from artiq.experiment import kernel, delay
from artiq.language.environment import HasEnvironment
from artiq.language.types import TList, TStr, TFloat, TBool
from artiq.language.units import ms

import inspect
import logging

import copy

logging.basicConfig()
logger = logging.getLogger(__name__)


[docs] def add_or_append(obj, member, value): if not hasattr(obj, member): setattr(obj, member, value) else: setattr(obj, member, getattr(obj, member) + value)
[docs] class Component(HasEnvironment): """An atomiq Component Every component in atomiq inherits from this class. It provides basic functionality for automatic and recursive building and initialization of components (prepare, build, prerun). It also takes care for joining kernel invariants along the inheritance tree. .. Note:: The arguments `parent` and `identifier` are automatically passed to the component object by the atomiq object builder. Args: parent: The parent context of the component. Usually this is the experiment that uses the component identifier: A unique name to identify the component. debug_output: Set whether the component should show debug output. Using this switch rather than the debug kernel logger can allow the compiler to not include the debug commands in the kernel code if the output is not needed. """ kernel_invariants = {"identifier", "core", "experiment", "debug_output"} def __init__(self, parent, identifier: TStr, debug_output: TBool = False, *args, **kwargs): self.experiment = parent.experiment super().__init__(self.experiment) self.identifier = identifier self.debug_output = debug_output self.core = self.experiment.core # combine kernel_invariants from parent class with ourselfs self._kernel_invariants = copy.copy(self.kernel_invariants) for cls in inspect.getmro(type(self))[1:]: if hasattr(cls, "kernel_invariants"): self._kernel_invariants.update(cls.kernel_invariants) logger.debug(f"{self.identifier}: Joined kernel_invariants: {self._kernel_invariants}") self._prerun_done = False self._prepare_done = False self._build_done = False def _recursive_prepare(self): for arg in self.__dict__: if isinstance(getattr(self, arg), Component): getattr(self, arg)._recursive_prepare() # use prepare state to set unified kernel invariants self.kernel_invariants = self._kernel_invariants if not self._prepare_done: if self.__class__._prepare != Component._prepare: logging.info(f"Doing prepare for {self.identifier}") self._prepare() self._prepare_done = True def _prepare(self): """ Specify here what should be done for this component in the prepare phase """ pass def _recursive_build(self): for arg in self.__dict__: if isinstance(getattr(self, arg), Component): getattr(self, arg)._recursive_build() if not self._build_done: if self.__class__._build != Component._build: logging.info(f"Doing build for {self.identifier}") self._build() self._build_done = True def _build(self): """ Specify here what should be done for this component in the build phase """ pass @kernel def _do_prerun(self): self.experiment.core.break_realtime() delay(0.5*ms) self._prerun() def _recursive_prerun(self): for arg in self.__dict__: if isinstance(getattr(self, arg), Component): getattr(self, arg)._recursive_prerun() if not self._prerun_done: if self.__class__._prerun != Component._prerun: logging.info(f"Doing prerun for {self.identifier}") self._do_prerun() self._prerun_done = True @kernel def _prerun(self): """ Specify here what should be done for this component before the run starts. In contrast to the _build() method, the _prerun() routine is executed on the core device before the actual experiment starts. """ pass
[docs] class Measurable(): kernel_invariants = {"channels"} def __init__(self, channels: TList(TStr)): """ A Measurable has one ore more channels at which data can be measured """ add_or_append(self, "channels", channels)
[docs] @kernel def measure(self, channel: TStr = "") -> TFloat: raise NotImplementedError("Implement measure method")
[docs] def measurement_channels(self): return self.channels
[docs] class Triggerable(): kernel_invariants = {"channels"} def __init__(self, channels: TList(TStr)): """ A Triggerable has one ore more channel(s) that can be triggered """ add_or_append(self, "channels", channels)
[docs] @kernel def fire(self, channel: TStr = ""): raise NotImplementedError("Implement fire() method")
[docs] class Switchable(): kernel_invariants = {"channels"} def __init__(self, channels: TList(TStr)): """ A Switchable has one ore more channel(s) that can be switched on or off """ add_or_append(self, "channels", channels)
[docs] @kernel def on(self, channel: TStr = None): raise NotImplementedError("Implement on() method")
[docs] @kernel def off(self, channel: TStr = None): raise NotImplementedError("Implement off() method")
[docs] @kernel def is_on(self, channel: TStr = None): raise NotImplementedError("Implement is_on() method")
[docs] @kernel def toggle(self, channel: TStr = None): if self.is_on(channel): self.off(channel) else: self.on(channel)
[docs] @kernel def pulse(self, pulsetime: TFloat, channel: TStr = ""): self.on() delay(pulsetime) self.off()
[docs] class Parametrizable(): kernel_invariants = {"channels"} def __init__(self, channels: TList(TStr)): """ A Parametrizable is an entity that can be controlled by one or more continuous parameter(s) """ add_or_append(self, "channels", channels)
[docs] @kernel def set_parameter(self, value: TFloat, channel: TStr = None): if channel is not None and hasattr(self, "set_"+channel): getattr(self, "set_"+channel)(value) else: raise NotImplementedError("Implement set_" + channel + "() method in class " + self.__class__.__name__ + " or an appropriate ancestor")