mirror of
https://github.com/m-labs/artiq.git
synced 2024-12-28 20:53:35 +08:00
simplify unit system and use floats by default
This commit is contained in:
parent
e6a4c2fb36
commit
c71fe29792
@ -1,6 +1,4 @@
|
||||
from artiq.language.core import *
|
||||
from artiq.language.experiment import Experiment
|
||||
from artiq.language.db import *
|
||||
from artiq.language.units import check_unit
|
||||
from artiq.language.units import ps, ns, us, ms, s
|
||||
from artiq.language.units import Hz, kHz, MHz, GHz
|
||||
from artiq.language.units import *
|
||||
|
@ -5,7 +5,6 @@ from artiq.language.db import *
|
||||
from artiq.language.units import ns
|
||||
|
||||
from artiq.transforms.inline import inline
|
||||
from artiq.transforms.lower_units import lower_units
|
||||
from artiq.transforms.quantize_time import quantize_time
|
||||
from artiq.transforms.remove_inter_assigns import remove_inter_assigns
|
||||
from artiq.transforms.fold_constants import fold_constants
|
||||
@ -61,13 +60,10 @@ class Core(AutoDB):
|
||||
|
||||
def transform_stack(self, func_def, rpc_map, exception_map,
|
||||
debug_unparse=_no_debug_unparse):
|
||||
lower_units(func_def, rpc_map)
|
||||
debug_unparse("lower_units", func_def)
|
||||
|
||||
remove_inter_assigns(func_def)
|
||||
debug_unparse("remove_inter_assigns_1", func_def)
|
||||
|
||||
quantize_time(func_def, self.ref_period.amount)
|
||||
quantize_time(func_def, self.ref_period)
|
||||
debug_unparse("quantize_time", func_def)
|
||||
|
||||
fold_constants(func_def)
|
||||
|
@ -2,7 +2,7 @@ import logging
|
||||
import ctypes
|
||||
import struct
|
||||
|
||||
from artiq.language.units import dB, check_unit, Quantity
|
||||
from artiq.language.units import dB
|
||||
|
||||
|
||||
logger = logging.getLogger("lda")
|
||||
@ -47,14 +47,7 @@ class Ldasim:
|
||||
"""
|
||||
|
||||
step = self.get_att_step_size()
|
||||
|
||||
if isinstance(attenuation, Quantity):
|
||||
check_unit(attenuation, "dB")
|
||||
att = attenuation
|
||||
else:
|
||||
att = attenuation*dB
|
||||
|
||||
att = round(att/step)*step
|
||||
att = round(attenuation/step)*step
|
||||
|
||||
if att > self.get_att_max():
|
||||
raise ValueError("Cannot set attenuation {} > {}"
|
||||
@ -62,7 +55,7 @@ class Ldasim:
|
||||
elif att < 0*dB:
|
||||
raise ValueError("Cannot set attenuation {} < 0".format(att))
|
||||
else:
|
||||
att = round(att.amount*4)/4. * dB
|
||||
att = round(att*4)/4. * dB
|
||||
self._attenuation = att
|
||||
|
||||
def ping(self):
|
||||
@ -218,14 +211,7 @@ class Lda:
|
||||
"""
|
||||
|
||||
step = self.get_att_step_size()
|
||||
|
||||
if isinstance(attenuation, Quantity):
|
||||
check_unit(attenuation, "dB")
|
||||
att = attenuation
|
||||
else:
|
||||
att = attenuation*dB
|
||||
|
||||
att = round(att/step)*step
|
||||
att = round(attenuation/step)*step
|
||||
|
||||
if att > self.get_att_max():
|
||||
raise ValueError("Cannot set attenuation {} > {}"
|
||||
@ -233,7 +219,7 @@ class Lda:
|
||||
elif att < 0*dB:
|
||||
raise ValueError("Cannot set attenuation {} < 0".format(att))
|
||||
else:
|
||||
self.set(0x8d, bytes([int(round(att.amount*4))]))
|
||||
self.set(0x8d, bytes([int(round(att*4))]))
|
||||
|
||||
def ping(self):
|
||||
try:
|
||||
|
@ -4,7 +4,7 @@ import struct as st
|
||||
|
||||
import serial
|
||||
|
||||
from artiq.language.units import V, strip_unit
|
||||
from artiq.language.units import V
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -302,7 +302,7 @@ class Tcube:
|
||||
class Tpz(Tcube):
|
||||
def __init__(self, serial_dev):
|
||||
Tcube.__init__(self, serial_dev)
|
||||
self.voltage_limit = self.get_tpz_io_settings()[0].amount
|
||||
self.voltage_limit = self.get_tpz_io_settings()[0]
|
||||
|
||||
def handle_message(self, msg):
|
||||
msg_id = msg.id
|
||||
@ -372,8 +372,6 @@ class Tpz(Tcube):
|
||||
between the three values 75 V, 100 V and 150 V.
|
||||
"""
|
||||
|
||||
voltage = strip_unit(voltage, "V")
|
||||
|
||||
if voltage < 0 or voltage > self.voltage_limit:
|
||||
raise ValueError("Voltage must be in range [0;{}]"
|
||||
.format(self.voltage_limit))
|
||||
@ -390,7 +388,7 @@ class Tpz(Tcube):
|
||||
|
||||
get_msg = self.send_request(MGMSG.PZ_REQ_OUTPUTVOLTS,
|
||||
[MGMSG.PZ_GET_OUTPUTVOLTS], 1)
|
||||
return st.unpack("<H", get_msg.data[2:])[0]*self.voltage_limit*V/32767
|
||||
return st.unpack("<H", get_msg.data[2:])[0]*self.voltage_limit/32767
|
||||
|
||||
def set_output_position(self, position_sw):
|
||||
"""Set output position of the piezo actuator.
|
||||
@ -532,7 +530,6 @@ class Tpz(Tcube):
|
||||
<artiq.devices.thorlabs.driver.Tpz.set_tpz_io_settings>` method.
|
||||
"""
|
||||
|
||||
output = strip_unit(output, "V")
|
||||
volt = round(output*32767/self.voltage_limit)
|
||||
payload = st.pack("<HHH", 1, lut_index, volt)
|
||||
self.send(Message(MGMSG.PZ_SET_OUTPUTLUT, data=payload))
|
||||
@ -548,7 +545,7 @@ class Tpz(Tcube):
|
||||
get_msg = self.send_request(MGMSG.PZ_REQ_OUTPUTLUT,
|
||||
[MGMSG.PZ_GET_OUTPUTLUT], 1)
|
||||
(index, output) = st.unpack("<Hh", get_msg.data[2:])
|
||||
return index, output*self.voltage_limit*V/32767
|
||||
return index, output*self.voltage_limit/32767
|
||||
|
||||
def set_output_lut_parameters(self, mode, cycle_length, num_cycles,
|
||||
delay_time, precycle_rest, postcycle_rest):
|
||||
@ -684,9 +681,6 @@ class Tpz(Tcube):
|
||||
100 V limit.
|
||||
|
||||
150 V limit.
|
||||
|
||||
You can either provide this parameter as an integer or as a
|
||||
:class:`artiq.language.units` Volt quantity (e.g. 75*V).
|
||||
:param hub_analog_input: When the T-Cube piezo driver unit is used in
|
||||
conjunction with the T-Cube Strain Gauge Reader (TSG001) on the
|
||||
T-Cube Controller Hub (TCH001), a feedback signal can be passed
|
||||
@ -706,7 +700,7 @@ class Tpz(Tcube):
|
||||
connectors.
|
||||
"""
|
||||
|
||||
self.voltage_limit = strip_unit(voltage_limit, "V")
|
||||
self.voltage_limit = voltage_limit
|
||||
|
||||
if self.voltage_limit == 75:
|
||||
voltage_limit = 1
|
||||
@ -727,21 +721,21 @@ class Tpz(Tcube):
|
||||
Hub analog input. Refer to :py:meth:`set_tpz_io_settings()
|
||||
<artiq.devices.thorlabs.driver.Tpz.set_tpz_io_settings>` for the
|
||||
meaning of those parameters.
|
||||
:rtype: a 2 elements tuple (Quantity, int)
|
||||
:rtype: a 2 elements tuple (int, int)
|
||||
"""
|
||||
|
||||
get_msg = self.send_request(MGMSG.PZ_REQ_TPZ_IOSETTINGS,
|
||||
[MGMSG.PZ_GET_TPZ_IOSETTINGS], 1)
|
||||
voltage_limit, hub_analog_input = st.unpack("<HH", get_msg.data[2:6])
|
||||
if voltage_limit == 1:
|
||||
voltage_limit = 75*V
|
||||
voltage_limit = 75
|
||||
elif voltage_limit == 2:
|
||||
voltage_limit = 100*V
|
||||
voltage_limit = 100
|
||||
elif voltage_limit == 3:
|
||||
voltage_limit = 150*V
|
||||
voltage_limit = 150
|
||||
else:
|
||||
raise ValueError("Voltage limit should be in range [1; 3]")
|
||||
self.voltage_limit = voltage_limit.amount
|
||||
self.voltage_limit = voltage_limit
|
||||
return voltage_limit, hub_analog_input
|
||||
|
||||
|
||||
@ -1398,14 +1392,13 @@ class TpzSim:
|
||||
return self.intensity
|
||||
|
||||
def set_tpz_io_settings(self, voltage_limit, hub_analog_input):
|
||||
self.voltage_limit = strip_unit(voltage_limit, "V")
|
||||
|
||||
if self.voltage_limit not in [75, 100, 150]:
|
||||
if voltage_limit not in [75, 100, 150]:
|
||||
raise ValueError("voltage_limit must be 75 V, 100 V or 150 V")
|
||||
self.voltage_limit = voltage_limit
|
||||
self.hub_analog_input = hub_analog_input
|
||||
|
||||
def get_tpz_io_settings(self):
|
||||
return self.voltage_limit*V, self.hub_analog_input
|
||||
return self.voltage_limit, self.hub_analog_input
|
||||
|
||||
|
||||
class TdcSim:
|
||||
|
@ -9,7 +9,6 @@ from pyqtgraph import dockarea
|
||||
|
||||
from artiq.tools import TaskObject
|
||||
from artiq.protocols.sync_struct import Subscriber
|
||||
from artiq.language.units import strip_unit
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -170,7 +169,7 @@ class _DeviceManager:
|
||||
if (v["module"] == "artiq.coredevice.dds"
|
||||
and v["class"] == "DDS"):
|
||||
channel = v["arguments"]["channel"]
|
||||
sysclk = strip_unit(v["arguments"]["sysclk"], "Hz")
|
||||
sysclk = v["arguments"]["sysclk"]
|
||||
self.dds_widgets[channel] = _DDSWidget(
|
||||
self.send_to_device, channel, sysclk, k)
|
||||
self.dds_cb()
|
||||
|
@ -231,7 +231,7 @@ def time_to_cycles(time, core=None):
|
||||
"""
|
||||
if core is None:
|
||||
raise ValueError("Core device must be specified for time conversion")
|
||||
return round64(time.amount//core.ref_period)
|
||||
return round64(time//core.ref_period)
|
||||
|
||||
|
||||
def cycles_to_time(cycles, core=None):
|
||||
|
@ -1,266 +1,17 @@
|
||||
"""
|
||||
Definition and management of physical units.
|
||||
|
||||
"""
|
||||
|
||||
from fractions import Fraction as _Fraction
|
||||
|
||||
|
||||
class DimensionError(Exception):
|
||||
"""Raised when attempting an operation with incompatible units.
|
||||
|
||||
When targeting the core device, all units are statically managed at
|
||||
compilation time. Thus, when raised by functions in this module, this
|
||||
exception cannot be caught in the kernel as it is raised by the compiler
|
||||
instead.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def mul_dimension(l, r):
|
||||
"""Returns the unit obtained by multiplying unit ``l`` with unit ``r``.
|
||||
|
||||
Raises ``DimensionError`` if the resulting unit is not implemented.
|
||||
|
||||
"""
|
||||
if l is None:
|
||||
return r
|
||||
if r is None:
|
||||
return l
|
||||
if {l, r} == {"Hz", "s"}:
|
||||
return None
|
||||
raise DimensionError
|
||||
|
||||
|
||||
def _rmul_dimension(l, r):
|
||||
return mul_dimension(r, l)
|
||||
|
||||
|
||||
def div_dimension(l, r):
|
||||
"""Returns the unit obtained by dividing unit ``l`` with unit ``r``.
|
||||
|
||||
Raises ``DimensionError`` if the resulting unit is not implemented.
|
||||
|
||||
"""
|
||||
if l == r:
|
||||
return None
|
||||
if r is None:
|
||||
return l
|
||||
if l is None:
|
||||
if r == "s":
|
||||
return "Hz"
|
||||
if r == "Hz":
|
||||
return "s"
|
||||
raise DimensionError
|
||||
|
||||
|
||||
def _rdiv_dimension(l, r):
|
||||
return div_dimension(r, l)
|
||||
|
||||
|
||||
def addsub_dimension(x, y):
|
||||
"""Returns the unit obtained by adding or subtracting unit ``l`` with
|
||||
unit ``r``.
|
||||
|
||||
Raises ``DimensionError`` if ``l`` and ``r`` are different.
|
||||
|
||||
"""
|
||||
if x == y:
|
||||
return x
|
||||
else:
|
||||
raise DimensionError
|
||||
|
||||
|
||||
_prefixes_str = "pnum_kMG"
|
||||
_smallest_prefix = _Fraction(1, 10**12)
|
||||
|
||||
|
||||
def _format(amount, unit):
|
||||
if amount is NotImplemented:
|
||||
return NotImplemented
|
||||
if unit is None:
|
||||
return amount
|
||||
else:
|
||||
return Quantity(amount, unit)
|
||||
|
||||
|
||||
class Quantity:
|
||||
"""Represents an amount in a given fundamental unit (identified by a
|
||||
string).
|
||||
|
||||
The amount can be of any Python numerical type (integer, float,
|
||||
Fraction, ...).
|
||||
Arithmetic operations and comparisons are directly delegated to the
|
||||
underlying numerical types.
|
||||
|
||||
"""
|
||||
def __init__(self, amount, unit):
|
||||
self.amount = amount
|
||||
self.unit = unit
|
||||
|
||||
def __repr__(self):
|
||||
r_amount = self.amount
|
||||
if isinstance(r_amount, int) or isinstance(r_amount, _Fraction):
|
||||
r_prefix = 0
|
||||
r_amount = r_amount/_smallest_prefix
|
||||
if r_amount:
|
||||
numerator = r_amount.numerator
|
||||
while numerator % 1000 == 0 and r_prefix < len(_prefixes_str):
|
||||
numerator /= 1000
|
||||
r_amount /= 1000
|
||||
r_prefix += 1
|
||||
prefix_str = _prefixes_str[r_prefix]
|
||||
if prefix_str == "_":
|
||||
prefix_str = ""
|
||||
return str(r_amount) + " " + prefix_str + self.unit
|
||||
else:
|
||||
return str(r_amount) + " " + self.unit
|
||||
|
||||
def __float__(self):
|
||||
return float(self.amount)
|
||||
|
||||
# mul/div
|
||||
def _binop(self, other, opf_name, dim_function):
|
||||
opf = getattr(self.amount, opf_name)
|
||||
if isinstance(other, Quantity):
|
||||
amount = opf(other.amount)
|
||||
unit = dim_function(self.unit, other.unit)
|
||||
else:
|
||||
amount = opf(other)
|
||||
unit = dim_function(self.unit, None)
|
||||
return _format(amount, unit)
|
||||
|
||||
def __mul__(self, other):
|
||||
return self._binop(other, "__mul__", mul_dimension)
|
||||
|
||||
def __rmul__(self, other):
|
||||
return self._binop(other, "__rmul__", _rmul_dimension)
|
||||
|
||||
def __truediv__(self, other):
|
||||
return self._binop(other, "__truediv__", div_dimension)
|
||||
|
||||
def __rtruediv__(self, other):
|
||||
return self._binop(other, "__rtruediv__", _rdiv_dimension)
|
||||
|
||||
def __floordiv__(self, other):
|
||||
return self._binop(other, "__floordiv__", div_dimension)
|
||||
|
||||
def __rfloordiv__(self, other):
|
||||
return self._binop(other, "__rfloordiv__", _rdiv_dimension)
|
||||
|
||||
# unary ops
|
||||
def __neg__(self):
|
||||
return Quantity(self.amount.__neg__(), self.unit)
|
||||
|
||||
def __pos__(self):
|
||||
return Quantity(self.amount.__pos__(), self.unit)
|
||||
|
||||
def __abs__(self):
|
||||
return Quantity(abs(self.amount), self.unit)
|
||||
|
||||
# add/sub
|
||||
def __add__(self, other):
|
||||
return self._binop(other, "__add__", addsub_dimension)
|
||||
|
||||
def __radd__(self, other):
|
||||
return self._binop(other, "__radd__", addsub_dimension)
|
||||
|
||||
def __sub__(self, other):
|
||||
return self._binop(other, "__sub__", addsub_dimension)
|
||||
|
||||
def __rsub__(self, other):
|
||||
return self._binop(other, "__rsub__", addsub_dimension)
|
||||
|
||||
def __mod__(self, other):
|
||||
return self._binop(other, "__mod__", addsub_dimension)
|
||||
|
||||
def __rmod__(self, other):
|
||||
return self._binop(other, "__rmod__", addsub_dimension)
|
||||
|
||||
# comparisons
|
||||
def _cmp(self, other, opf_name):
|
||||
if not isinstance(other, Quantity) or other.unit != self.unit:
|
||||
raise DimensionError
|
||||
return getattr(self.amount, opf_name)(other.amount)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._cmp(other, "__lt__")
|
||||
|
||||
def __le__(self, other):
|
||||
return self._cmp(other, "__le__")
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._cmp(other, "__eq__")
|
||||
|
||||
def __ne__(self, other):
|
||||
return self._cmp(other, "__ne__")
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._cmp(other, "__gt__")
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._cmp(other, "__ge__")
|
||||
_smallest_prefix_exp = -12
|
||||
|
||||
|
||||
def _register_unit(unit, prefixes):
|
||||
amount = _smallest_prefix
|
||||
exponent = _smallest_prefix_exp
|
||||
for prefix in _prefixes_str:
|
||||
if prefix in prefixes:
|
||||
quantity = Quantity(amount, unit)
|
||||
full_name = prefix + unit if prefix != "_" else unit
|
||||
globals()[full_name] = quantity
|
||||
amount *= 1000
|
||||
globals()[full_name] = 10.**exponent
|
||||
exponent += 3
|
||||
|
||||
|
||||
_register_unit("s", "pnum_")
|
||||
_register_unit("Hz", "_kMG")
|
||||
_register_unit("dB", "_")
|
||||
_register_unit("V", "um_k")
|
||||
|
||||
|
||||
def check_unit(value, unit):
|
||||
"""Checks that the value has the specified unit. Unit specification is
|
||||
a string representing the unit without any prefix (e.g. ``s``, ``Hz``).
|
||||
Checking for a dimensionless value (not a ``Quantity`` instance) is done
|
||||
by setting ``unit`` to ``None``.
|
||||
|
||||
If the units do not match, ``DimensionError`` is raised.
|
||||
|
||||
This function can be used in kernels and is executed at compilation time.
|
||||
|
||||
There is already unit checking built into the arithmetic, so you typically
|
||||
need to use this function only when using the ``amount`` property of
|
||||
``Quantity``.
|
||||
|
||||
"""
|
||||
if unit is None:
|
||||
if isinstance(value, Quantity):
|
||||
raise DimensionError
|
||||
else:
|
||||
if not isinstance(value, Quantity) or value.unit != unit:
|
||||
raise DimensionError
|
||||
|
||||
def strip_unit(value, unit):
|
||||
"""Check that the value has the specified unit and returns its amount.
|
||||
Raises ``DimensionError`` if the units does not match.
|
||||
|
||||
If the passed value is not a ``Quantity``, it is assumed to be in the
|
||||
specified unit and is returned unchanged.
|
||||
|
||||
If ``unit`` is ``None``, passing a ``Quantity`` as value raises
|
||||
``DimensionError``.
|
||||
|
||||
"""
|
||||
if unit is None:
|
||||
if isinstance(value, Quantity):
|
||||
raise DimensionError
|
||||
else:
|
||||
return value
|
||||
else:
|
||||
if isinstance(value, Quantity):
|
||||
if value.unit != unit:
|
||||
raise DimensionError
|
||||
else:
|
||||
return value.amount
|
||||
else:
|
||||
return value
|
||||
|
@ -6,7 +6,6 @@ import traceback
|
||||
import time
|
||||
|
||||
from artiq.protocols import pyon
|
||||
from artiq.language.units import strip_unit
|
||||
from artiq.tools import (asyncio_process_wait_timeout, asyncio_process_wait,
|
||||
asyncio_wait_or_cancel)
|
||||
|
||||
@ -50,7 +49,7 @@ class Worker:
|
||||
avail = set(range(n_user_watchdogs + 1)) \
|
||||
- set(self.watchdogs.keys())
|
||||
wid = next(iter(avail))
|
||||
self.watchdogs[wid] = time.monotonic() + strip_unit(t, "s")
|
||||
self.watchdogs[wid] = time.monotonic() + t
|
||||
return wid
|
||||
|
||||
def delete_watchdog(self, wid):
|
||||
|
@ -14,7 +14,6 @@ The main rationale for this new custom serializer (instead of using JSON) is
|
||||
that JSON does not support Numpy and more generally cannot be extended with
|
||||
other data types while keeping a concise syntax. Here we can use the Python
|
||||
function call syntax to mark special data types.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@ -25,8 +24,6 @@ import tempfile
|
||||
|
||||
import numpy
|
||||
|
||||
from artiq.language.units import Quantity
|
||||
|
||||
|
||||
_encode_map = {
|
||||
type(None): "none",
|
||||
@ -39,7 +36,6 @@ _encode_map = {
|
||||
list: "list",
|
||||
dict: "dict",
|
||||
Fraction: "fraction",
|
||||
Quantity: "quantity",
|
||||
numpy.ndarray: "nparray"
|
||||
}
|
||||
|
||||
@ -110,10 +106,6 @@ class _Encoder:
|
||||
return "Fraction({}, {})".format(encode(x.numerator),
|
||||
encode(x.denominator))
|
||||
|
||||
def encode_quantity(self, x):
|
||||
return "Quantity({}, {})".format(encode(x.amount),
|
||||
encode(x.unit))
|
||||
|
||||
def encode_nparray(self, x):
|
||||
r = "nparray("
|
||||
r += encode(x.shape) + ", "
|
||||
@ -147,7 +139,6 @@ _eval_dict = {
|
||||
"true": True,
|
||||
|
||||
"Fraction": Fraction,
|
||||
"Quantity": Quantity,
|
||||
"nparray": _nparray
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import os
|
||||
from fractions import Fraction
|
||||
|
||||
from artiq import *
|
||||
from artiq.language.units import DimensionError
|
||||
from artiq.coredevice import comm_tcp, core, runtime_exceptions, ttl
|
||||
from artiq.sim import devices as sim_devices
|
||||
|
||||
@ -51,7 +50,6 @@ class _Primes(AutoDB):
|
||||
class _Misc(AutoDB):
|
||||
def build(self):
|
||||
self.input = 84
|
||||
self.inhomogeneous_units = []
|
||||
self.al = [1, 2, 3, 4, 5]
|
||||
self.list_copy_in = [2*Hz, 10*MHz]
|
||||
|
||||
@ -59,29 +57,10 @@ class _Misc(AutoDB):
|
||||
def run(self):
|
||||
self.half_input = self.input//2
|
||||
self.decimal_fraction = Fraction("1.2")
|
||||
self.inhomogeneous_units.append(1000*Hz)
|
||||
self.inhomogeneous_units.append(10*s)
|
||||
self.acc = 0
|
||||
for i in range(len(self.al)):
|
||||
self.acc += self.al[i]
|
||||
self.list_copy_out = self.list_copy_in
|
||||
self.unit_comp = [1*MHz for _ in range(3)]
|
||||
|
||||
@kernel
|
||||
def dimension_error1(self):
|
||||
print(1*Hz + 1*s)
|
||||
|
||||
@kernel
|
||||
def dimension_error2(self):
|
||||
print(1*Hz < 1*s)
|
||||
|
||||
@kernel
|
||||
def dimension_error3(self):
|
||||
check_unit(1*Hz, "s")
|
||||
|
||||
@kernel
|
||||
def dimension_error4(self):
|
||||
delay(10*Hz)
|
||||
|
||||
|
||||
class _PulseLogger(AutoDB):
|
||||
@ -103,9 +82,9 @@ class _PulseLogger(AutoDB):
|
||||
|
||||
@kernel
|
||||
def pulse(self, f, duration):
|
||||
self.on(int(now().amount*1000000000), f)
|
||||
self.on(int(now()*1000000000), f)
|
||||
delay(duration)
|
||||
self.off(int(now().amount*1000000000))
|
||||
self.off(int(now()*1000000000))
|
||||
|
||||
|
||||
class _Pulses(AutoDB):
|
||||
@ -229,18 +208,8 @@ class ExecutionCase(unittest.TestCase):
|
||||
uut.run()
|
||||
self.assertEqual(uut.half_input, 42)
|
||||
self.assertEqual(uut.decimal_fraction, Fraction("1.2"))
|
||||
self.assertEqual(uut.inhomogeneous_units, [1000*Hz, 10*s])
|
||||
self.assertEqual(uut.acc, sum(uut.al))
|
||||
self.assertEqual(uut.list_copy_in, uut.list_copy_out)
|
||||
self.assertEqual(uut.unit_comp, [1*MHz for _ in range(3)])
|
||||
with self.assertRaises(DimensionError):
|
||||
uut.dimension_error1()
|
||||
with self.assertRaises(DimensionError):
|
||||
uut.dimension_error2()
|
||||
with self.assertRaises(DimensionError):
|
||||
uut.dimension_error3()
|
||||
with self.assertRaises(DimensionError):
|
||||
uut.dimension_error4()
|
||||
finally:
|
||||
comm.close()
|
||||
|
||||
|
@ -10,9 +10,9 @@ lda_serial = os.getenv("ARTIQ_LDA_SERIAL")
|
||||
|
||||
class GenericLdaTest:
|
||||
def test_attenuation(self):
|
||||
step = self.cont.get_att_step_size().amount
|
||||
max = self.cont.get_att_max().amount
|
||||
test_vector = [i*step*dB for i in range(0, int(max*int(1/step)+1))]
|
||||
step = self.cont.get_att_step_size()
|
||||
attmax = self.cont.get_att_max()
|
||||
test_vector = [i*step*dB for i in range(0, int(attmax*int(1/step)+1))]
|
||||
for i in test_vector:
|
||||
with self.subTest(i=i):
|
||||
self.cont.set_attenuation(i)
|
||||
|
@ -88,7 +88,7 @@ class GenericTpzTest:
|
||||
|
||||
def test_ouput_volts(self):
|
||||
for voltage in 5*V, 10*V, 15*V, \
|
||||
round(self.cont.get_tpz_io_settings()[0].amount)*V:
|
||||
round(self.cont.get_tpz_io_settings()[0])*V:
|
||||
with self.subTest(voltage=voltage):
|
||||
test_vector = voltage
|
||||
self.cont.set_output_volts(test_vector)
|
||||
|
@ -6,21 +6,13 @@ from artiq.coredevice import comm_dummy, core
|
||||
from artiq.transforms.unparse import unparse
|
||||
|
||||
|
||||
# Original code before inline:
|
||||
#
|
||||
# n = time_to_cycles(1.2345*ns)
|
||||
# ftw = self.dds.frequency_to_ftw(345*MHz)
|
||||
# f = self.dds.ftw_to_frequency(ftw)
|
||||
# phi = 1000*cycles_to_time(n)*f
|
||||
# do_someting(int(phi))
|
||||
#
|
||||
optimize_in = """
|
||||
|
||||
def run():
|
||||
dds_sysclk = Quantity(Fraction(1000000000, 1), 'Hz')
|
||||
n = time_to_cycles((1.2345 * Quantity(Fraction(1, 1000000000), 's')))
|
||||
dds_sysclk = Fraction(1000000000, 1)
|
||||
n = time_to_cycles((1.2345 * Fraction(1, 1000000000)))
|
||||
with sequential:
|
||||
frequency = (345 * Quantity(Fraction(1000000, 1), 'Hz'))
|
||||
frequency = 345 * Fraction(1000000, 1)
|
||||
frequency_to_ftw_return = int((((2 ** 32) * frequency) / dds_sysclk))
|
||||
ftw = frequency_to_ftw_return
|
||||
with sequential:
|
||||
|
@ -1,190 +0,0 @@
|
||||
import ast
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
|
||||
from artiq.language import units
|
||||
from artiq.transforms.tools import embeddable_func_names
|
||||
|
||||
|
||||
def _add_units(f, unit_list):
|
||||
def wrapper(*args):
|
||||
new_args = []
|
||||
for arg, unit in zip(args, unit_list):
|
||||
if unit is None:
|
||||
new_args.append(arg)
|
||||
else:
|
||||
if isinstance(arg, list):
|
||||
new_args.append([units.Quantity(x, unit) for x in arg])
|
||||
else:
|
||||
new_args.append(units.Quantity(arg, unit))
|
||||
return f(*new_args)
|
||||
return wrapper
|
||||
|
||||
|
||||
class _UnitsLowerer(ast.NodeTransformer):
|
||||
def __init__(self, rpc_map):
|
||||
self.rpc_map = rpc_map
|
||||
# (original rpc number, (unit list)) -> new rpc number
|
||||
self.rpc_remap = defaultdict(lambda: len(self.rpc_remap))
|
||||
self.variable_units = dict()
|
||||
|
||||
def visit_Name(self, node):
|
||||
try:
|
||||
unit = self.variable_units[node.id]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
if unit is not None:
|
||||
node.unit = unit
|
||||
return node
|
||||
|
||||
def visit_BoolOp(self, node):
|
||||
self.generic_visit(node)
|
||||
us = [getattr(value, "unit", None) for value in node.values]
|
||||
if not all(u == us[0] for u in us[1:]):
|
||||
raise units.DimensionError
|
||||
return node
|
||||
|
||||
def visit_Compare(self, node):
|
||||
self.generic_visit(node)
|
||||
u0 = getattr(node.left, "unit", None)
|
||||
us = [getattr(comparator, "unit", None)
|
||||
for comparator in node.comparators]
|
||||
if not all(u == u0 for u in us):
|
||||
raise units.DimensionError
|
||||
return node
|
||||
|
||||
def visit_UnaryOp(self, node):
|
||||
self.generic_visit(node)
|
||||
if hasattr(node.operand, "unit"):
|
||||
node.unit = node.operand.unit
|
||||
return node
|
||||
|
||||
def visit_BinOp(self, node):
|
||||
self.generic_visit(node)
|
||||
op = type(node.op)
|
||||
left_unit = getattr(node.left, "unit", None)
|
||||
right_unit = getattr(node.right, "unit", None)
|
||||
if op in (ast.Add, ast.Sub, ast.Mod):
|
||||
unit = units.addsub_dimension(left_unit, right_unit)
|
||||
elif op == ast.Mult:
|
||||
unit = units.mul_dimension(left_unit, right_unit)
|
||||
elif op in (ast.Div, ast.FloorDiv):
|
||||
unit = units.div_dimension(left_unit, right_unit)
|
||||
else:
|
||||
if left_unit is not None or right_unit is not None:
|
||||
raise units.DimensionError
|
||||
unit = None
|
||||
if unit is not None:
|
||||
node.unit = unit
|
||||
return node
|
||||
|
||||
def visit_Attribute(self, node):
|
||||
self.generic_visit(node)
|
||||
if node.attr == "amount" and hasattr(node.value, "unit"):
|
||||
del node.value.unit
|
||||
return node.value
|
||||
else:
|
||||
return node
|
||||
|
||||
def visit_List(self, node):
|
||||
self.generic_visit(node)
|
||||
if node.elts:
|
||||
us = [getattr(elt, "unit", None) for elt in node.elts]
|
||||
if not all(u == us[0] for u in us[1:]):
|
||||
raise units.DimensionError
|
||||
node.unit = us[0]
|
||||
return node
|
||||
|
||||
def visit_ListComp(self, node):
|
||||
self.generic_visit(node)
|
||||
if hasattr(node.elt, "unit"):
|
||||
node.unit = node.elt.unit
|
||||
return node
|
||||
|
||||
def visit_Call(self, node):
|
||||
self.generic_visit(node)
|
||||
if node.func.id == "Quantity":
|
||||
amount, unit = node.args
|
||||
amount.unit = unit.s
|
||||
return amount
|
||||
elif node.func.id in ("now", "cycles_to_time"):
|
||||
node.unit = "s"
|
||||
elif node.func.id == "syscall":
|
||||
# only RPCs can have units
|
||||
if node.args[0].s == "rpc":
|
||||
unit_list = tuple(getattr(arg, "unit", None)
|
||||
for arg in node.args[2:])
|
||||
rpc_n = node.args[1].n
|
||||
node.args[1].n = self.rpc_remap[(rpc_n, (unit_list))]
|
||||
else:
|
||||
if any(hasattr(arg, "unit") for arg in node.args):
|
||||
raise units.DimensionError
|
||||
elif node.func.id in ("delay", "at", "time_to_cycles", "watchdog"):
|
||||
if getattr(node.args[0], "unit", None) != "s":
|
||||
raise units.DimensionError
|
||||
elif node.func.id == "check_unit":
|
||||
self.generic_visit(node)
|
||||
elif node.func.id in embeddable_func_names:
|
||||
# must be last (some embeddable funcs may have units)
|
||||
if any(hasattr(arg, "unit") for arg in node.args):
|
||||
raise units.DimensionError
|
||||
return node
|
||||
|
||||
def visit_Expr(self, node):
|
||||
self.generic_visit(node)
|
||||
if (isinstance(node.value, ast.Call)
|
||||
and node.value.func.id == "check_unit"):
|
||||
call = node.value
|
||||
if (isinstance(call.args[1], ast.NameConstant)
|
||||
and call.args[1].value is None):
|
||||
if hasattr(call.value.args[0], "unit"):
|
||||
raise units.DimensionError
|
||||
elif isinstance(call.args[1], ast.Str):
|
||||
if getattr(call.args[0], "unit", None) != call.args[1].s:
|
||||
raise units.DimensionError
|
||||
else:
|
||||
raise NotImplementedError
|
||||
return None
|
||||
else:
|
||||
return node
|
||||
|
||||
def _update_target(self, target, unit):
|
||||
if isinstance(target, ast.Name):
|
||||
if target.id in self.variable_units:
|
||||
if self.variable_units[target.id] != unit:
|
||||
raise TypeError(
|
||||
"Inconsistent units for variable '{}': '{}' and '{}'"
|
||||
.format(target.id,
|
||||
self.variable_units[target.id],
|
||||
unit))
|
||||
else:
|
||||
self.variable_units[target.id] = unit
|
||||
|
||||
def visit_Assign(self, node):
|
||||
node.value = self.visit(node.value)
|
||||
unit = getattr(node.value, "unit", None)
|
||||
for target in node.targets:
|
||||
self._update_target(target, unit)
|
||||
return node
|
||||
|
||||
def visit_AugAssign(self, node):
|
||||
value = self.visit_BinOp(ast.BinOp(
|
||||
op=node.op, left=node.target, right=node.value))
|
||||
unit = getattr(value, "unit", None)
|
||||
self._update_target(node.target, unit)
|
||||
return node
|
||||
|
||||
# Only dimensionless iterators are supported
|
||||
def visit_For(self, node):
|
||||
self.generic_visit(node)
|
||||
self._update_target(node.target, None)
|
||||
return node
|
||||
|
||||
|
||||
def lower_units(func_def, rpc_map):
|
||||
ul = _UnitsLowerer(rpc_map)
|
||||
ul.visit(func_def)
|
||||
original_map = copy(rpc_map)
|
||||
for (original_rpcn, unit_list), new_rpcn in ul.rpc_remap.items():
|
||||
rpc_map[new_rpcn] = _add_units(original_map[original_rpcn], unit_list)
|
@ -11,7 +11,7 @@ embeddable_funcs = (
|
||||
core_language.syscall, core_language.watchdog,
|
||||
range, bool, int, float, round, len,
|
||||
core_language.int64, core_language.round64,
|
||||
Fraction, units.Quantity, units.check_unit, core_language.EncodedException
|
||||
Fraction, core_language.EncodedException
|
||||
)
|
||||
embeddable_func_names = {func.__name__ for func in embeddable_funcs}
|
||||
|
||||
@ -61,11 +61,6 @@ def value_to_ast(value):
|
||||
for kg in core_language.kernel_globals:
|
||||
if value is getattr(core_language, kg):
|
||||
return ast.Name(kg, ast.Load())
|
||||
if isinstance(value, units.Quantity):
|
||||
return ast.Call(
|
||||
func=ast.Name("Quantity", ast.Load()),
|
||||
args=[value_to_ast(value.amount), ast.Str(value.unit)],
|
||||
keywords=[], starargs=None, kwargs=None)
|
||||
raise NotASTRepresentable(str(value))
|
||||
|
||||
|
||||
@ -88,14 +83,6 @@ def eval_constant(node):
|
||||
numerator = eval_constant(node.args[0])
|
||||
denominator = eval_constant(node.args[1])
|
||||
return Fraction(numerator, denominator)
|
||||
elif funcname == "Quantity":
|
||||
amount, unit = node.args
|
||||
amount = eval_constant(amount)
|
||||
try:
|
||||
unit = getattr(units, unit.id)
|
||||
except:
|
||||
raise NotConstant
|
||||
return units.Quantity(amount, unit)
|
||||
else:
|
||||
raise NotConstant
|
||||
else:
|
||||
@ -105,8 +92,7 @@ def eval_constant(node):
|
||||
_replaceable_funcs = {
|
||||
"bool", "int", "float", "round",
|
||||
"int64", "round64", "Fraction",
|
||||
"time_to_cycles", "cycles_to_time",
|
||||
"Quantity"
|
||||
"time_to_cycles", "cycles_to_time"
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ class RPCTiming(Experiment, AutoDB):
|
||||
t1 = self.core.get_rtio_time()
|
||||
self.nop(10)
|
||||
t2 = self.core.get_rtio_time()
|
||||
self.ts[i] = float(t2.amount - t1.amount)
|
||||
self.ts[i] = t2 - t1
|
||||
|
||||
def run(self):
|
||||
self.bench()
|
||||
|
@ -60,22 +60,19 @@
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
|
||||
"channel": 0}
|
||||
"arguments": {"sysclk": 1e9, "channel": 0}
|
||||
},
|
||||
"dds1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
|
||||
"channel": 1}
|
||||
"arguments": {"sysclk": 1e9, "channel": 1}
|
||||
},
|
||||
"dds2": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
|
||||
"channel": 2}
|
||||
"arguments": {"sysclk": 1e9, "channel": 2}
|
||||
},
|
||||
|
||||
"qc_q1_0": {
|
||||
|
Loading…
Reference in New Issue
Block a user