From 0dac8b8ab2cb8148787f6d3c8d761e279dbdba40 Mon Sep 17 00:00:00 2001 From: francof2a Date: Thu, 10 Nov 2022 13:06:18 -0300 Subject: [PATCH] Fix complex dtype detection (#67) and index in equal (#66) change _qfmt and _fxpfmt to methods --- fxpmath/objects.py | 55 ++++++++++++++++++++++++++------------------ tests/test_issues.py | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/fxpmath/objects.py b/fxpmath/objects.py index 723955e..2a149cc 100644 --- a/fxpmath/objects.py +++ b/fxpmath/objects.py @@ -315,12 +315,14 @@ def get_dtype(self, notation=None): self._update_dtype(notation) # update dtype return self._dtype - _qfmt = re.compile(r'(s|u|q|uq|qu)(\d+)(\.\d+)?') - _fxpfmt = re.compile(r'fxp-(s|u)(\d+)/(\d+)(-complex)?') + def _qfmt(self): + return re.compile(r'(s|u|q|uq|qu)(\d+)(\.\d+)?') + def _fxpfmt(self): + return re.compile(r'fxp-(s|u)(\d+)/(\d+)(-complex)?') def _parseformatstr(self, fmt): fmt = fmt.casefold() - mo = self._qfmt.match(fmt) + mo = self._qfmt().match(fmt) if mo: # Q/S notation counts the sign bit as an integer bit, such that # the total number of bits is always int+frac @@ -333,7 +335,7 @@ def _parseformatstr(self, fmt): n_word = n_frac + n_int complex_dtype = False else: - mo = self._fxpfmt.match(fmt) + mo = self._fxpfmt().match(fmt) if mo: signed = mo.group(1) == 's' n_word = int(mo.group(2)) @@ -570,12 +572,12 @@ def reshape(self, shape, order='C'): If an integer, then the result will be a 1-D array of that length. One shape dimension can be -1. In this case, the value is inferred from the length of the array and remaining dimensions. - order : {‘C’, ‘F’, ‘A’}, optional + order : {'C', 'F', 'A'}, optional Read the elements of a using this index order, and place the elements into the reshaped array using this index order. - ‘C’ means to read / write the elements using C-like index order, with the last axis index changing fastest, back to the first axis index changing slowest. - ‘F’ means to read / write the elements using Fortran-like index order, with the first index changing fastest, and the last index changing slowest. - Note that the ‘C’ and ‘F’ options take no account of the memory layout of the underlying array, and only refer to the order of indexing. - ‘A’ means to read / write the elements in Fortran-like index order if a is Fortran contiguous in memory, C-like order otherwise. + 'C' means to read / write the elements using C-like index order, with the last axis index changing fastest, back to the first axis index changing slowest. + 'F' means to read / write the elements using Fortran-like index order, with the first index changing fastest, and the last index changing slowest. + Note that the 'C' and 'F' options take no account of the memory layout of the underlying array, and only refer to the order of indexing. + 'A' means to read / write the elements in Fortran-like index order if a is Fortran contiguous in memory, C-like order otherwise. Returns --- @@ -594,12 +596,12 @@ def flatten(self, order='C'): Parameters --- - order{‘C’, ‘F’, ‘A’, ‘K’}, optional - ‘C’ means to flatten in row-major (C-style) order. - ‘F’ means to flatten in column-major (Fortran- style) order. - ‘A’ means to flatten in column-major order if a is Fortran contiguous in memory, row-major order otherwise. - ‘K’ means to flatten a in the order the elements occur in memory. - The default is ‘C’. + order{'C', 'F', 'A', 'K'}, optional + 'C' means to flatten in row-major (C-style) order. + 'F' means to flatten in column-major (Fortran- style) order. + 'A' means to flatten in column-major order if a is Fortran contiguous in memory, row-major order otherwise. + 'K' means to flatten a in the order the elements occur in memory. + The default is 'C'. Returns --- @@ -750,7 +752,9 @@ def _update_dtype(self, notation=None): self._dtype = 'fxp-{sign}{nword}/{nfrac}{comp}'.format(sign='s' if self.signed else 'u', nword=self.n_word, nfrac=self.n_frac, - comp='-complex' if (self.val.dtype == complex or self.vdtype == complex) else '') + comp='-complex' if (isinstance(self.val, complex) or \ + self.val.dtype == complex or \ + self.vdtype == complex) else '') else: self._dtype = 'fxp-{sign}{nword}/{nfrac}'.format(sign='s' if self.signed else 'u', nword=self.n_word, @@ -1014,7 +1018,7 @@ def uraw(self): """ return np.where(self.val < 0, (1 << self.n_word) + self.val, self.val) - def equal(self, x): + def equal(self, x, index=None): """ Sets the value of the Fxp using the value of other Fxp object. If `x` is not a Fxp, this method set the value just like `set_val` method. @@ -1025,6 +1029,9 @@ def equal(self, x): x : Fxp object, None, int, float, complex, list of numbers, numpy array, str (bin, hex, dec) Value(s) to be stored in fractional fixed-point (base 2) format. + index : int, optional, default=None + Index of the element to be overwritten in list or array of values by `val` input. + Returns --- @@ -1033,10 +1040,15 @@ def equal(self, x): """ if isinstance(x, Fxp): - new_val_raw = x.val * 2**(self.n_frac - x.n_frac) - self.set_val(new_val_raw, raw=True) + if index is None: + raw_val = x.val[index] + else: + raw_val = x.val + + new_val_raw = raw_val * 2**(self.n_frac - x.n_frac) + self.set_val(new_val_raw, raw=True, index=index) else: - self.set_val(x) + self.set_val(x, index=index) return self # behaviors @@ -1857,9 +1869,6 @@ def item(self, *args): items = args[0] return self.astype(item=items) - # ToDo: - # nonzero - def clip(self, a_min=None, a_max=None, **kwargs): from .functions import clip diff --git a/tests/test_issues.py b/tests/test_issues.py index 15cec87..b980158 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -274,3 +274,55 @@ def test_issue_62_v0_4_7(): y[0][0] = y[0][0] + 1.0 assert y[0][0]() == 0.0 + +def test_issue_66_v0_4_8(): + x = Fxp(np.array([1.25, 0.5]), dtype='S8.4') + y = Fxp(np.array([2.25, 1.5]), dtype='S16.6') + # x[0].equal(y[0]) # it does NOT work + # x[0] = y[0] # it works + x.equal(y[0], index=0) # it works + + assert x[0]() == y[0]() + +def test_issue_67_v0_4_8(): + input_size = Fxp(None, dtype='fxp-s32/23') + f = [0,10+7j,20-0.65j,30] + f = Fxp(f, like = input_size) + + def FFT(f): + N = len(f) + if N <= 1: + return f + + # division: decompose N point FFT into N/2 point FFT + even= FFT(f[0::2]) + odd = FFT(f[1::2]) + + # store combination of results + temp = np.zeros(N, dtype=complex) + # temp = Fxp(temp, dtype='fxp-s65/23') + temp = Fxp(temp, dtype='fxp-s65/46') + + for u in range(N//2): + W = Fxp(np.exp(-2j*np.pi*u/N), like=input_size) + temp[u] = even[u] + W* odd[u] + temp[u+N//2] = even[u] - W*odd[u] + + return temp + + # testing the function to see if it matches the manual computation + F_fft = FFT(f) + +def test_issue_73_v0_4_8(): + # single unsigned value does work + a = Fxp(10, False, 14, 3) + b = Fxp(15, False, 14, 3) + c = a - b + assert c() == 0.0 # 0.0 --> correct + + # unsigned list does not work + d = Fxp([10, 21], False, 14, 3) + e = Fxp([15, 15], False, 14, 3) + f = d - e + assert f[0]() == 0.0 # [4095.875 6.0] --> 4095.875 is the upper limit + assert f[1]() == 6.0 \ No newline at end of file