artiq/artiq/language/units.py

176 lines
4.6 KiB
Python
Raw Normal View History

from fractions import Fraction as _Fraction
2014-05-17 20:08:50 +08:00
2014-09-05 12:03:22 +08:00
2014-08-13 17:52:01 +08:00
_prefixes_str = "pnum_kMG"
_smallest_prefix = _Fraction(1, 10**12)
2014-05-17 20:08:50 +08:00
def mul_dimension(l, r):
if l is None:
return r
if r is None:
return l
if {l, r} == {"Hz", "s"}:
return None
def _rmul_dimension(l, r):
return mul_dimension(r, l)
def div_dimension(l, r):
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"
def _rdiv_dimension(l, r):
return div_dimension(r, l)
def addsub_dimension(x, y):
if x == y:
return x
else:
return None
def _format(amount, unit):
if amount is NotImplemented:
return NotImplemented
if unit is None:
return amount
else:
return Quantity(amount, unit)
2014-09-05 12:03:22 +08:00
2014-05-17 20:08:50 +08:00
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.
"""
2014-09-05 12:03:22 +08:00
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):
2014-09-05 12:03:22 +08:00
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 = ""
2014-10-05 21:35:24 +08:00
return str(r_amount) + " " + prefix_str + self.unit
2014-09-05 12:03:22 +08:00
else:
2014-10-05 21:35:24 +08:00
return str(r_amount) + " " + self.unit
2014-09-05 12:03:22 +08:00
# mul/div
def _binop(self, other, opf_name, dim_function):
opf = getattr(self.amount, opf_name)
2014-09-05 12:03:22 +08:00
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)
2014-09-05 12:03:22 +08:00
def __rmul__(self, other):
return self._binop(other, "__rmul__", _rmul_dimension)
2014-09-05 12:03:22 +08:00
def __truediv__(self, other):
return self._binop(other, "__truediv__", div_dimension)
def __rtruediv__(self, other):
return self._binop(other, "__rtruediv__", _rdiv_dimension)
2014-09-05 12:03:22 +08:00
def __floordiv__(self, other):
return self._binop(other, "__floordiv__", div_dimension)
def __rfloordiv__(self, other):
return self._binop(other, "__rfloordiv__", _rdiv_dimension)
2014-09-05 12:03:22 +08:00
# unary ops
def __neg__(self):
return Quantity(self.amount.__neg__(), self.unit)
2014-09-05 12:03:22 +08:00
def __pos__(self):
return Quantity(self.amount.__pos__(), self.unit)
2014-09-05 12:03:22 +08:00
# add/sub
def __add__(self, other):
return self._binop(other, "__add__", addsub_dimension)
2014-09-05 12:03:22 +08:00
def __radd__(self, other):
return self._binop(other, "__radd__", addsub_dimension)
2014-09-05 12:03:22 +08:00
def __sub__(self, other):
return self._binop(other, "__sub__", addsub_dimension)
2014-09-05 12:03:22 +08:00
def __rsub__(self, other):
return self._binop(other, "__rsub__", addsub_dimension)
2014-09-05 12:03:22 +08:00
2014-10-06 23:26:35 +08:00
def __mod__(self, other):
return self._binop(other, "__mod__", addsub_dimension)
def __rmod__(self, other):
return self._binop(other, "__rmod__", addsub_dimension)
2014-09-05 12:03:22 +08:00
# comparisons
def _cmp(self, other, opf_name):
if isinstance(other, Quantity):
other = other.amount
return getattr(self.amount, opf_name)(other)
2014-09-05 12:03:22 +08:00
def __lt__(self, other):
return self._cmp(other, "__lt__")
2014-09-05 12:03:22 +08:00
def __le__(self, other):
return self._cmp(other, "__le__")
2014-09-05 12:03:22 +08:00
def __eq__(self, other):
return self._cmp(other, "__eq__")
2014-09-05 12:03:22 +08:00
def __ne__(self, other):
return self._cmp(other, "__ne__")
2014-09-05 12:03:22 +08:00
def __gt__(self, other):
return self._cmp(other, "__gt__")
2014-09-05 12:03:22 +08:00
def __ge__(self, other):
return self._cmp(other, "__ge__")
2014-09-05 12:03:22 +08:00
2014-05-17 20:08:50 +08:00
def _register_unit(unit, prefixes):
2014-09-05 12:03:22 +08:00
amount = _smallest_prefix
for prefix in _prefixes_str:
if prefix in prefixes:
quantity = Quantity(amount, unit)
full_name = prefix + unit if prefix != "_" else unit
2014-09-05 12:03:22 +08:00
globals()[full_name] = quantity
amount *= 1000
2014-05-17 20:08:50 +08:00
2014-08-13 17:52:01 +08:00
_register_unit("s", "pnum_")
_register_unit("Hz", "_kMG")