"""
In all experiments, calibrations are ubiquious. Examples are
* voltage - power relation on a photodiode
* Relation between the current through a coil and the created magentic field
* RF power in an AOM and light power in the diffracted order
* The current-voltage relation for a voltage-controlled current supply
* ...
In atomiq calibrations are comoponents just like every other piece of your experiment. Every calibration inherits
from the abstract :class:`Calibration` class. A special subclass of calibration functions are invertable
calibrations that can be analytically inverted. The most frequently used example is a linear calibration function.
Invertable calibrations inherit from :class:`InvertableCalibration`.
"""
from __future__ import annotations
from atomiq.components.primitives import Component
from artiq.experiment import portable
from artiq.language.types import TList, TFloat, TStr, TBool
[docs]
@portable
def exp(x: TFloat) -> TFloat:
return 2.7182818**x
[docs]
@portable
def ln(x: TFloat, n: TFloat = 10000.0) -> TFloat:
return n * ((x ** (1/n)) - 1)
[docs]
class Calibration(Component):
"""An abstract Calibration
This is an abstract class to describe a calibration.
Args:
input_unit: A string determining the input unit (e.g. 'mW', 'V', or 'uA')
output_unit: A string determining the output unit (e.g. 'mW', 'V', or 'uA')
"""
kernel_invariants = {"input_unit", "output_unit"}
def __init__(self, input_unit: TStr, output_unit: TStr, *args, **kwargs):
Component.__init__(self, *args, **kwargs)
self.input_unit = input_unit
self.output_unit = output_unit
[docs]
class InvertableCalibration(Calibration):
[docs]
class DummyCalibration(Calibration):
def __init__(self, *args, **kwargs):
Calibration.__init__(self, "", "", *args, **kwargs)
[docs]
class SplineCalibration(Calibration):
"""Calibration via data points
Data points are interpolated with linear splines
Args:
calibration_points: List of tuples (x, y) containing the calibration data. The data must be ordered
monotonously in `x`
"""
kernel_invariants = {"calibration_points"}
def __init__(self, calibration_points: TList(TList(TFloat)), *args, **kwargs):
Calibration.__init__(self, *args, **kwargs)
self.calibration_points = calibration_points
[docs]
class InvertableSplineCalibration(InvertableCalibration, SplineCalibration):
"""Calibration via data points that can be inverted
Data points are interpolated with linear splines. For the inversion to work, both `x` and `y` of the calibration
data must be monotonous.
"""
def __init__(self, *args, **kwargs):
InvertableCalibration.__init__(self, *args, **kwargs)
SplineCalibration.__init__(self, *args, **kwargs)
[docs]
class PolynomialCalibration(Calibration):
"""Calibration described by a polynomial
The calibration is given by the function
$$f(x) = \\sum_i c_i x^i$$
Args:
coefficients: List of coefficients $c_i$ of the polynomial, start from the lowest order.
"""
kernel_invariants = {"coefficients"}
def __init__(self, *args, coefficients: TList(TFloat), **kwargs):
Calibration.__init__(self, *args, **kwargs)
self.coefficients = coefficients
[docs]
class LinearCalibration(InvertableCalibration):
"""Linear calibration
The calibration is given by the function
$$f(x) = ax + b$$
Args:
input_unit: Unit of the input
output_unit: Unit of the output
a: Calibration coefficient a
b: Calibration coefficient b
"""
kernel_invariants = {"a", "b"}
def __init__(self, a: TFloat, b: TFloat, *args, **kwargs):
InvertableCalibration.__init__(self, *args, **kwargs)
self.a = a
self.b = b
[docs]
class SigmoidCalibration(Calibration):
"""Sigmoid calibration
```
output = A / ( 1 + e^k*(input - x_offset)) + y_offset
```
Args:
input_unit: Unit of the input
output_unit: Unit of the output
A: Amplitude of the sigmoid
k: stretching of the sigmoid
x_offset: offset on the x axis
y_offset: offset on the y axis
"""
kernel_invariants = {"A", "k", "x_offset", "y_offset"}
def __init__(self, *args, A: TFloat, k: TFloat, x_offset: TFloat = 0, y_offset: TFloat = 0, **kwargs):
Calibration.__init__(self, *args, **kwargs)
self.A = A
self.k = k
self.x_offset = x_offset
self.y_offset = y_offset
[docs]
class InvSigmoidCalibration(Calibration):
"""Inverse sigmoid calibration
```
output = -ln( A / (input - y_offset) - 1) / k + x_offset
```
The paremeters are defined in a way that they match the sigmoid definition.
Args:
input_unit: Unit of the input
output_unit: Unit of the output
A: Amplitude of the sigmoid
k: stretching of the sigmoid
x_offset: offset on the x axis
y_offset: offset on the y axis
"""
kernel_invariants = {"A", "k", "x_offset", "y_offset"}
def __init__(self, *args, A: TFloat, k: TFloat, x_offset: TFloat = 0, y_offset: TFloat = 0, **kwargs):
Calibration.__init__(self, *args, **kwargs)
self.A = A
self.k = k
self.x_offset = x_offset
self.y_offset = y_offset