def multiply(self, x, y): # Use 2 * wordlength long multipliers z = Representation(mantissa=x.mantissa * y.mantissa, format_=Format(msb=self.format_.msb * 2 + 1, lsb=self.format_.lsb * 2, signed=self.format_.signed)) mantissa, (underflow, overflow) = self.format_.represent( z, rounding_method=self.rounding_method) if underflow and not self.multiply.allows_underflow: raise UnderflowError(f'{x} * {y} underflows in {self.format_}', mpfloat(z), self.format_.value_epsilon) if overflow: if not self.multiply.allows_overflow: raise OverflowError(f'{x} * {y} overflows in {self.format_}', mpfloat(z), self.format_.value_interval) mantissa, _ = self.overflow_behavior( mantissa, range_=self.format_.mantissa_interval) return type(x)(mantissa, self.format_)
def __le__(self, other): if not other.is_Number: return super().__le__(other) if not other: return self._args[0] <= 0 if isinstance(other, type(self)): return self._args[0] <= other._args[0] if isinstance(self._args[0], Basic): return self._args[0] <= other return self._args[0] <= mpfloat(other)
def __eq__(self, other): if not other.is_Number: return False if not other: return not self._args[0] if isinstance(other, type(self)): return self._args[0] == other._args[0] if isinstance(self._args[0], Basic): return self._args[0] == other return self._args[0] == mpfloat(other)
def computation_error_bounds(self, input_range, state_ranges=None): if state_ranges is None and self.states: state_ranges = [] for model in self.state_observer_models: state_range = output_range(model, mpfloat(input_range)) state_range = interval( lower_bound=asvector_if_possible(state_range.lower_bound), upper_bound=asvector_if_possible(state_range.upper_bound), ) state_ranges.append(state_range) return super().computation_error_bounds([input_range], state_ranges)
def nearest(self, x): if x.is_integer: return x value = nearest_integer(mpfloat(x)) mantissa, (_, overflow) = self.format_.represent(value) if overflow: if not self.nearest.allows_overflow: raise OverflowError(f'round({x}) overflows in {self.format_}', mantissa * self.format_.value_epsilon, self.format_.value_interval) mantissa, _ = self.overflow_behavior( mantissa, range_=self.format_.mantissa_interval) return type(x)(mantissa, format_)
def __mul__(self, other): if not isinstance(other, Number): other = Number(other) result = self.number * other.number snumber = mpfloat(self.number) onumber = mpfloat(other.number) result_error_bounds = ((snumber + self.error_bounds) * (onumber + other.error_bounds)).difference( snumber * onumber) if isinstance(result, (FixedPointNumber, FixedPointSymbol)): exact = (self.number == -1 or self.number == 1 or self.number == 0 or other.number == -1 or other.number == 1 or other.number == 0) if not exact: rounding = ProcessingUnit.active().rounding_method if isinstance(self.number, (FixedPointNumber, FixedPointSymbol)) and \ isinstance(other.number, (FixedPointNumber, FixedPointSymbol)): result_error_bounds += rounding.error_bounds( result.format_.lsb, self.number.format_.lsb + other.number.format_.lsb) else: result_error_bounds += rounding.error_bounds( result.format_.lsb) return Number(result, result_error_bounds)
def represent(self, rvalue, rounding_method=round): from ltitop.arithmetic.fixed_point.representation import Representation if isinstance(rvalue, Representation) and hasattr( rounding_method, 'shift'): lvalue = rvalue.mantissa quantize = functools.partial(rounding_method.shift, n=rvalue.format_.lsb - self.lsb) else: lvalue = mpfloat(rvalue) quantize = functools.partial(mpquantize, nbits=-self.lsb, rounding_method=rounding_method) if not self.signed and not np.all(lvalue >= 0): raise ValueError(f'Unsigned format cannot represent {rvalue}') mantissa = quantize(lvalue) underflow = bool(np.any(np.logical_and(mantissa == 0, lvalue != 0))) overflow = bool(np.any(mantissa not in self.mantissa_interval)) return mantissa, (underflow, overflow)
def best(cls, value, wordlength, rounding_method=nearest_integer, signed=True): """ See "Reliable Implementation of Linear Filters with Fixed-Point Arithmetic", Hilaire and Lopez, 2013 """ # TODO(hidmic): optimize when value is a Representation with mpmath.workprec(20 * wordlength): # enough bits mpvalue = mpfloat(value) if not np.all(mpvalue >= 0) and not signed: raise ValueError(f'Unsigned format cannot represent {value}') # estimate MSB msb = np.max(mpmsb(mpvalue, signed=signed)) if np.isneginf(msb): msb = 0 # arbitrary msb = int(msb) # estimate LSB lsb = msb - wordlength + int(signed) # compute mantissa mantissa = mpquantize(mpvalue, nbits=-lsb, rounding_method=rounding_method) # adjust MSB if limits were overpassed adjusted_msb = msb # no adjustment upper_limit = 2**(wordlength - int(signed)) if not np.all(mantissa < upper_limit): adjusted_msb = msb + 1 if np.all(mantissa < 0): lower_limit = -2**(wordlength - 2) if np.all(mantissa > lower_limit): adjusted_msb = msb - 1 if adjusted_msb != msb: # adjust LSB and mantissa msb = adjusted_msb lsb = msb - wordlength + int(signed) mantissa = mpquantize(mpvalue, nbits=-lsb, rounding_method=mpmath.nint) return mantissa, cls(msb=msb, lsb=lsb, signed=signed)
def _as_mpf_val(self, prec): with mpmath.workprec(prec): return mpfloat(self._args[0])
class Number: number: Any error_bounds: Interval = Interval(mpfloat(0)) def __add__(self, other): if not isinstance(other, Number): other = Number(other) result = self.number + other.number result_error_bounds = self.error_bounds + other.error_bounds if isinstance(result, (FixedPointNumber, FixedPointSymbol)): exact = self.number == 0 or other.number == 0 if not exact: rounding = ProcessingUnit.active().rounding_method if isinstance(self.number, (FixedPointNumber, FixedPointSymbol)): if result.format_.lsb > self.number.format_.lsb: result_error_bounds += rounding.error_bounds( result.format_.lsb, self.number.format_.lsb) elif self.number != 0: result_error_bounds += rounding.error_bounds( result.format_.lsb) if isinstance(other.number, (FixedPointNumber, FixedPointSymbol)): if result.format_.lsb > other.number.format_.lsb: result_error_bounds += rounding.error_bounds( result.format_.lsb, other.number.format_.lsb) elif other.number != 0: result_error_bounds += rounding.error_bounds( result.format_.lsb) return Number(result, result_error_bounds) __radd__ = __add__ def __sub__(self, other): if not isinstance(other, Number): other = Number(other) result = self.number - other.number result_error_bounds = self.error_bounds + other.error_bounds if isinstance(result, (FixedPointNumber, FixedPointSymbol)): exact = self.number == 0 or other.number == 0 if not exact: rounding = ProcessingUnit.active().rounding_method if isinstance(self.number, (FixedPointNumber, FixedPointSymbol)): if result.format_.lsb > self.number.format_.lsb: result_error_bounds += rounding.error_bounds( result.format_.lsb, self.number.format_.lsb) elif self.number != 0: result_error_bounds += rounding.error_bounds( result.format_.lsb) if isinstance(other.number, (FixedPointNumber, FixedPointSymbol)): if result.format_.lsb > other.number.format_.lsb: result_error_bounds += rounding.error_bounds( result.format_.lsb, other.number.format_.lsb) elif other.number != 0: result_error_bounds += rounding.error_bounds( result.format_.lsb) return Number(result, result_error_bounds) def __rsub__(self, other): return Number(other) - self def __mul__(self, other): if not isinstance(other, Number): other = Number(other) result = self.number * other.number snumber = mpfloat(self.number) onumber = mpfloat(other.number) result_error_bounds = ((snumber + self.error_bounds) * (onumber + other.error_bounds)).difference( snumber * onumber) if isinstance(result, (FixedPointNumber, FixedPointSymbol)): exact = (self.number == -1 or self.number == 1 or self.number == 0 or other.number == -1 or other.number == 1 or other.number == 0) if not exact: rounding = ProcessingUnit.active().rounding_method if isinstance(self.number, (FixedPointNumber, FixedPointSymbol)) and \ isinstance(other.number, (FixedPointNumber, FixedPointSymbol)): result_error_bounds += rounding.error_bounds( result.format_.lsb, self.number.format_.lsb + other.number.format_.lsb) else: result_error_bounds += rounding.error_bounds( result.format_.lsb) return Number(result, result_error_bounds) __rmul__ = __mul__ def __div__(self, other): # TODO(hidmic): implement return NotImplemented __truediv__ = __div__ def __rdiv__(self, other): return Number(other) / self __rtruediv__ = __rdiv__ def __mod__(self, other): # TODO(hidmic): implement return NotImplemented def __rmod__(self, other): return Number(other) % self def __apply_rounding_method__(self, method): number = method(self.number) error_bounds = self.error_bounds lsb = None if isinstance(self.number, (FixedPointNumber, FixedPointSymbol)): lsb = self.number.format_.lsb error_bounds += method.error_bounds(0, lsb) return Number(number, error_bounds) def __trunc__(self): return self.__apply_rounding_method__(truncate) def __ceil__(self): return self.__apply_rounding_method__(ceil) def __floor__(self): return self.__apply_rounding_method__(floor) def __round__(self): return self.__apply_rounding_method__(nearest_integer) def __neg__(self): return Number(-self.number, -self.error_bounds) def __float__(self): return float(self.number) def __mpfloat__(self): return mpfloat(self.number) def __int__(self): return int(self.number) __long__ = __int__ def __nonzero__(self): return bool(self.number) __bool__ = __nonzero__ _iterable = False # tell sympy to not iterate this def __getitem__(self, key): try: error_bounds = self.error_bounds[key] except TypeError: error_bounds = self.error_bounds return Number(self.number[key], error_bounds) def __eq__(self, other): if not isinstance(other, Number): other = Number(other) return self.number == other.number and \ self.error_bounds == other.error_bounds == 0 def __ne__(self, other): return not (self == other) def __lt__(self, other): if not isinstance(other, Number): other = Number(other) return mpfloat(self.number) + self.error_bounds < \ mpfloat(other.number) + other.error_bounds def __le__(self, other): return self < other or self == other def __gt__(self, other): return not (self <= other) def __ge__(self, other): return not (self < other) def __lshift__(self, n): return Number(self.number << n, self.error_bounds * 2**n) def __rshift__(self, n): # TODO(hidmic): implement return NotImplemented
def __lt__(self, other): if not isinstance(other, Number): other = Number(other) return mpfloat(self.number) + self.error_bounds < \ mpfloat(other.number) + other.error_bounds
def __mpfloat__(self): return mpfloat(self.number)