diff --git a/changelog.txt b/changelog.txt index d0105dc..0a8adde 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,14 @@ +version 0.4.3 +-------------------------------------------------- +* Fxp adds `config` as optional init parameter (kwarg). +* Functions (operators) over one and two variables copy first operator configuration by default. +* Changes above fixed wrap overflowing bug after operation (issue #42 solved). +* Wrap function of utils fix bug when data has more than n_word_max (64 bits in the most of cases) (issue #41 soved). +* Config new methods: copy, deepcopy. +* Fix warning about internal complex check (issue #39). +* Add link implementation between `functions.truediv` with `numpy.divide`. +* Add `axes` parameter to `transpose` method. + version 0.4.2 -------------------------------------------------- * Add template to Config object. diff --git a/fxpmath/__init__.py b/fxpmath/__init__.py index 3f3f7ae..2faaa11 100644 --- a/fxpmath/__init__.py +++ b/fxpmath/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.4.2' +__version__ = '0.4.3' import sys import os diff --git a/fxpmath/functions.py b/fxpmath/functions.py index 5cdd115..4778e90 100644 --- a/fxpmath/functions.py +++ b/fxpmath/functions.py @@ -99,6 +99,8 @@ def _function_over_one_var(repr_func, raw_func, x, out=None, out_like=None, sizi if not out.signed and signed: raise ValueError('Signed addition can not be stored in unsigned `out` object!') n_frac = out.n_frac + config = None + elif out_like is not None: if not isinstance(out_like, Fxp): raise TypeError('`out_like` must be a Fxp object!') @@ -107,6 +109,10 @@ def _function_over_one_var(repr_func, raw_func, x, out=None, out_like=None, sizi signed = None n_frac = None n_int = None + config = None + + else: + config = x.config if method == 'repr' or x.scaled or n_frac is None: raw = False @@ -141,6 +147,8 @@ def _function_over_two_vars(repr_func, raw_func, x, y, out=None, out_like=None, if not out.signed and signed: raise ValueError('Signed addition can not be stored in unsigned `out` object!') n_frac = out.n_frac + config = None + elif out_like is not None: if not isinstance(out_like, Fxp): raise TypeError('`out_like` must be a Fxp object!') @@ -149,6 +157,10 @@ def _function_over_two_vars(repr_func, raw_func, x, y, out=None, out_like=None, signed = None n_frac = None n_int = None + config = None + + else: + config = x.config if method == 'repr' or x.scaled or n_frac is None: raw = False @@ -163,7 +175,7 @@ def _function_over_two_vars(repr_func, raw_func, x, y, out=None, out_like=None, if out is not None: z = out.set_val(val, raw=raw) else: - z = Fxp(val, signed=signed, n_int=n_int, n_frac=n_frac, like=out_like, raw=raw) + z = Fxp(val, signed=signed, n_int=n_int, n_frac=n_frac, like=out_like, raw=raw, config=config) return z @@ -374,7 +386,7 @@ def _floordiv_raw(x, y, n_frac): return _function_over_two_vars(repr_func=_floordiv_repr, raw_func=_floordiv_raw, x=x, y=y, out=out, out_like=out_like, sizing=sizing, method=method, optimal_size=optimal_size, **kwargs) -@implements(np.true_divide) +@implements(np.true_divide, np.divide) def truediv(x, y, out=None, out_like=None, sizing='optimal', method='raw', **kwargs): """ """ @@ -572,13 +584,14 @@ def _conjugate_raw(x, n_frac, **kwargs): return _function_over_one_var(repr_func=np.conjugate, raw_func=_conjugate_raw, x=x, out=out, out_like=out_like, sizing=sizing, method=method, **kwargs) @implements(np.transpose) -def transpose(x, out=None, out_like=None, sizing='optimal', method='raw', **kwargs): +def transpose(x, axes=None, out=None, out_like=None, sizing='optimal', method='raw', **kwargs): """ """ def _transpose_raw(x, n_frac, **kwargs): precision_cast = (lambda m: np.array(m, dtype=object)) if n_frac >= _n_word_max else (lambda m: m) return (x.val.T) * precision_cast(2**(n_frac - x.n_frac)) + kwargs['axes'] = axes return _function_over_one_var(repr_func=np.transpose, raw_func=_transpose_raw, x=x, out=out, out_like=out_like, sizing=sizing, method=method, **kwargs) @implements(np.clip) diff --git a/fxpmath/objects.py b/fxpmath/objects.py index eed4ac7..1a52d6f 100644 --- a/fxpmath/objects.py +++ b/fxpmath/objects.py @@ -149,6 +149,14 @@ def __init__(self, val=None, signed=None, n_word=None, n_frac=None, n_int=None, 'inaccuracy': False, 'extended_prec': False} + # update config as argument + _config = kwargs.pop('config', None) + if _config is not None: + if isinstance(_config, Config): + self.config = _config.deepcopy() + else: + raise TypeError('config parameter must be Config class type!') + # update config from kwargs self.config.update(**kwargs) @@ -594,7 +602,7 @@ def set_val(self, val, raw=False, vdtype=None, index=None): conv_factor = self._get_conv_factor(raw) # round, saturate and store - if original_vdtype != complex and not np.issubdtype(original_vdtype, complex): + if original_vdtype != complex and not np.issubdtype(original_vdtype, np.complexfloating): # val_dtype determination _n_word_max_ = min(_n_word_max, 64) if np.max(val) >= 2**_n_word_max_ or np.min(val) < -2**_n_word_max_ or self.n_word >= _n_word_max_: @@ -1522,7 +1530,7 @@ def T(self): x.val = x.val.T return x - def transpose(self, **kwargs): + def transpose(self, axes=None, **kwargs): from .functions import transpose out = kwargs.pop('out', self.config.op_out) @@ -1530,7 +1538,7 @@ def transpose(self, **kwargs): sizing = kwargs.pop('sizing', self.config.op_sizing) method = kwargs.pop('method', self.config.op_method) - return transpose(self, out=out, out_like=out_like, sizing=sizing, method=method, **kwargs) + return transpose(self, axes=axes, out=out, out_like=out_like, sizing=sizing, method=method, **kwargs) def item(self, *args): if len(args) > 1: @@ -1894,7 +1902,12 @@ def update(self, **kwargs): if hasattr(self, k): setattr(self, k, v) + # copy + def copy(self): + return copy.copy(self) + def deepcopy(self): + return copy.deepcopy(self) # endregion # ---------------------------------------------------------------------------------------- diff --git a/fxpmath/utils.py b/fxpmath/utils.py index 6d8a759..06de9b7 100644 --- a/fxpmath/utils.py +++ b/fxpmath/utils.py @@ -34,6 +34,7 @@ #%% import numpy as np +from . import _n_word_max #%% def array_support(func): @@ -312,13 +313,20 @@ def int_clip(x, val_min, val_max): x_clipped = np.array(max(val_min, min(val_max, int(x)))) return x_clipped -def wrap(x, signed, n_word): +def wrap(x, signed, n_word): + if n_word >= _n_word_max: + dtype = object + else: + dtype = int + m = (1 << n_word) if signed: - x = np.array(x).astype(int) & (m - 1) + x = np.array(x).astype(dtype) & (m - 1) + x = np.asarray(x).astype(dtype) x = np.where(x < (1 << (n_word-1)), x, x | (-m)) else: - x = np.array(x).astype(int) & (m - 1) + x = np.array(x).astype(dtype) & (m - 1) + x = np.asarray(x).astype(dtype) return x def get_sizes_from_dtype(dtype): diff --git a/tests/test_issues.py b/tests/test_issues.py index ff2948f..e259806 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -146,3 +146,15 @@ def test_issue_31_v0_4_0(): q3 = t(0.125*2**3) assert q3.val.dtype == object assert q3() == 1.0 + +def test_issue_41_v0_4_2(): + x = Fxp(2, False, 63, 0, overflow='wrap') + y = Fxp(2, False, 64, 0, overflow='wrap') + + assert x() == 2 + assert y() == 2 + +def test_issue_42_v0_4_2(): + b = Fxp(2, True, 4, 0, overflow='wrap') + assert (b + 8)() == -6.0 + assert (b - 8)() == -6.0 \ No newline at end of file