[pypy-commit] pypy numpypy-complex2: add ndarray.real, ndarray.imag; properly promote to complex; move rAlmostEqual out of space

mattip noreply at buildbot.pypy.org
Thu Sep 13 00:14:17 CEST 2012


Author: mattip <matti.picus at gmail.com>
Branch: numpypy-complex2
Changeset: r57306:207ac3dd1b78
Date: 2012-09-13 01:13 +0300
http://bitbucket.org/pypy/pypy/changeset/207ac3dd1b78/

Log:	add ndarray.real, ndarray.imag; properly promote to complex; move
	rAlmostEqual out of space

diff --git a/pypy/module/micronumpy/interp_boxes.py b/pypy/module/micronumpy/interp_boxes.py
--- a/pypy/module/micronumpy/interp_boxes.py
+++ b/pypy/module/micronumpy/interp_boxes.py
@@ -295,27 +295,6 @@
 
 class W_ComplexFloatingBox(W_InexactBox):
     _attrs_ = ()
-
-class W_Complex64Box(ComplexBox, W_ComplexFloatingBox):
-    descr__new__, _get_dtype = new_dtype_getter("complex64")
-    _COMPONENTS_BOX = W_Float32Box
-
-    def descr_get_real(self, space):
-        dtype = W_Float32Box._get_dtype(space)
-        box = self.convert_real_to(dtype)
-        assert isinstance(box, W_Float32Box)
-        return space.wrap(box)
-
-    def descr_get_imag(self, space):
-        dtype = W_Float32Box._get_dtype(space)
-        box = self.convert_imag_to(dtype)
-        assert isinstance(box, W_Float32Box)
-        return space.wrap(box)
-
-class W_Complex128Box(ComplexBox, W_ComplexFloatingBox):
-    descr__new__, _get_dtype = new_dtype_getter("complex128")
-    _COMPONENTS_BOX = W_Float64Box
-
     def descr_get_real(self, space):
         dtype = self._COMPONENTS_BOX._get_dtype(space)
         box = self.convert_real_to(dtype)
@@ -328,8 +307,17 @@
         assert isinstance(box, self._COMPONENTS_BOX)
         return space.wrap(box)
 
+
+class W_Complex64Box(ComplexBox, W_ComplexFloatingBox):
+    descr__new__, _get_dtype = new_dtype_getter("complex64")
+    _COMPONENTS_BOX = W_Float32Box
+
+
+class W_Complex128Box(ComplexBox, W_ComplexFloatingBox):
+    descr__new__, _get_dtype = new_dtype_getter("complex128")
+    _COMPONENTS_BOX = W_Float64Box
+
     
-
 W_GenericBox.typedef = TypeDef("generic",
     __module__ = "numpypy",
 
@@ -520,13 +508,13 @@
 W_Complex128Box.typedef = TypeDef("complex128", (W_ComplexFloatingBox.typedef, complex_typedef),
     __module__ = "numpypy",
     __new__ = interp2app(W_Complex128Box.descr__new__.im_func),
-    real = GetSetProperty(W_Complex128Box.descr_get_real),
-    imag = GetSetProperty(W_Complex128Box.descr_get_imag),
+    real = GetSetProperty(W_ComplexFloatingBox.descr_get_real),
+    imag = GetSetProperty(W_ComplexFloatingBox.descr_get_imag),
 )
 
 W_Complex64Box.typedef = TypeDef("complex64", (W_ComplexFloatingBox.typedef),
     __module__ = "numpypy",
     __new__ = interp2app(W_Complex64Box.descr__new__.im_func),
-    real = GetSetProperty(W_Complex64Box.descr_get_real),
-    imag = GetSetProperty(W_Complex64Box.descr_get_imag),
+    real = GetSetProperty(W_ComplexFloatingBox .descr_get_real),
+    imag = GetSetProperty(W_ComplexFloatingBox.descr_get_imag),
 )
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -305,6 +305,8 @@
     descr_neg = _unaryop_impl("negative")
     descr_abs = _unaryop_impl("absolute")
     descr_invert = _unaryop_impl("invert")
+    descr_get_real = _unaryop_impl("real")
+    descr_get_imag = _unaryop_impl("imag")
 
     def descr_nonzero(self, space):
         if self.get_size() > 1:
@@ -551,6 +553,8 @@
     swapaxes = interp2app(W_NDimArray.descr_swapaxes),
     flat = GetSetProperty(W_NDimArray.descr_get_flatiter),
     item = interp2app(W_NDimArray.descr_item),
+    real = GetSetProperty(W_NDimArray.descr_get_real),
+    imag = GetSetProperty(W_NDimArray.descr_get_imag),
 
     __array_interface__ = GetSetProperty(W_NDimArray.descr_array_iface),
 )
@@ -585,8 +589,9 @@
         for w_elem in elems_w:
             dtype = interp_ufuncs.find_dtype_for_scalar(space, w_elem,
                                                         dtype)
-            if dtype is interp_dtype.get_dtype_cache(space).w_float64dtype:
-                break
+            #if dtype is interp_dtype.get_dtype_cache(space).w_float64dtype:
+            #    break
+            
         if dtype is None:
             dtype = interp_dtype.get_dtype_cache(space).w_float64dtype
     if ndmin > len(shape):
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -354,6 +354,14 @@
     # Some operations promote op(bool, bool) to return int8, rather than bool
     if promote_bools and (dt1.kind == dt2.kind == interp_dtype.BOOLLTR):
         return interp_dtype.get_dtype_cache(space).w_int8dtype
+
+    # Everything promotes to complex
+    if dt2.num == 14 or dt2.num == 15 or dt1.num == 14 or dt2.num == 15:
+        if dt2.num == 15 or dt1.num == 15:
+            return interp_dtype.get_dtype_cache(space).w_complex128dtype
+        else:
+            return interp_dtype.get_dtype_cache(space).w_complex64dtype
+    
     if promote_to_float:
         return find_unaryop_result_dtype(space, dt2, promote_to_float=True)
     # If they're the same kind, choose the greater one.
@@ -428,7 +436,6 @@
     int64_dtype = interp_dtype.get_dtype_cache(space).w_int64dtype
     complex_type = interp_dtype.get_dtype_cache(space).w_complex128dtype
     float_type = interp_dtype.get_dtype_cache(space).w_float64dtype
-
     if isinstance(w_obj, interp_boxes.W_GenericBox):
         dtype = w_obj.get_dtype(space)
         if current_guess is None:
@@ -455,7 +462,8 @@
             current_guess is complex_type or current_guess is float_type):
             return complex_type
         return current_guess
-
+    if current_guess is complex_type:
+        return complex_type
     return interp_dtype.get_dtype_cache(space).w_float64dtype
 
 
@@ -529,6 +537,8 @@
             ("signbit", "signbit", 1, {"bool_result": True}),
             ("reciprocal", "reciprocal", 1),
             ("conjugate", "conj", 1),
+            ("real", "real", 1),
+            ("imag", "imag", 1),
 
             ("fabs", "fabs", 1, {"promote_to_float": True,
                                  "allow_complex": False}),
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -2,58 +2,8 @@
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
 
 from math import isnan, isinf, copysign
-from sys import version_info
 from pypy.conftest import option
 
-def rAlmostEqual(a, b, rel_err = 2e-15, abs_err = 5e-323, msg=''):
-    """Fail if the two floating-point numbers are not almost equal.
-
-    Determine whether floating-point values a and b are equal to within
-    a (small) rounding error.  The default values for rel_err and
-    abs_err are chosen to be suitable for platforms where a float is
-    represented by an IEEE 754 double.  They allow an error of between
-    9 and 19 ulps.
-    """
-
-    # special values testing
-    if isnan(a):
-        if isnan(b):
-            return True,''
-        raise AssertionError(msg + '%r should be nan' % (b,))
-
-    if isinf(a):
-        if a == b:
-            return True,''
-        raise AssertionError(msg + 'finite result where infinity expected: '+ \
-                          'expected %r, got %r' % (a, b))
-
-    # if both a and b are zero, check whether they have the same sign
-    # (in theory there are examples where it would be legitimate for a
-    # and b to have opposite signs; in practice these hardly ever
-    # occur).
-    if not a and not b:
-        # only check it if we are running on top of CPython >= 2.6
-        if version_info >= (2, 6) and copysign(1., a) != copysign(1., b):
-            raise AssertionError( msg + \
-                    'zero has wrong sign: expected %r, got %r' % (a, b))
-
-    # if a-b overflows, or b is infinite, return False.  Again, in
-    # theory there are examples where a is within a few ulps of the
-    # max representable float, and then b could legitimately be
-    # infinite.  In practice these examples are rare.
-    try:
-        absolute_error = abs(b-a)
-    except OverflowError:
-        pass
-    else:
-        # test passes if either the absolute error or the relative
-        # error is sufficiently small.  The defaults amount to an
-        # error of between 9 ulps and 19 ulps on an IEEE-754 compliant
-        # machine.
-        if absolute_error <= max(abs_err, rel_err * abs(a)):
-            return True,''
-    raise AssertionError(msg + '%r and %r are not sufficiently close' % (a, b))
-
 class AppTestUfuncs(BaseNumpyAppTest):
     def setup_class(cls):
         import os
@@ -62,9 +12,6 @@
         fname64 = os.path.join(os.path.dirname(__file__), 'complex64_testcases.txt')
         cls.w_testcases128 = cls.space.wrap(fname128)
         cls.w_testcases64 = cls.space.wrap(fname64)
-        def cls_rAlmostEqual(self, *args, **kwargs):
-            return rAlmostEqual(*args, **kwargs)
-        cls.w_rAlmostEqual = cls.space.wrap(cls_rAlmostEqual)
         cls.w_runAppDirect = cls.space.wrap(option.runappdirect)
         cls.w_isWindows = cls.space.wrap(os.name == 'nt')
 
@@ -235,6 +182,8 @@
                a[4 ], a[5 ], a[6 ], a[7 ],
                a[8 ], a[9 ], a[10], a[11],
                b[12], b[13]]
+        r2 = fmax(a,b)
+        r3 = (r2 == res)
         assert (fmax(a, b) == res).all()
         b = [inf]*a.size
         res = [b[0 ], b[1 ], a[2 ], b[3 ], 
@@ -388,20 +337,10 @@
                     -0j, 0j, cnan, 
                     cnan, cnan, cnan]
         for c, rel_err in ((complex64, 2e-7), (complex128, 2e-15), ):
-            actual = reciprocal(array(orig, dtype=c))
+            actual = reciprocal(array([orig], dtype=c))
             for b, a, e in zip(orig, actual, expected):
-                error_message = (
-                    'reciprocal(%r(%r, %r))\n'
-                    'Expected: complex(%r, %r)\n'
-                    'Received: complex(%r, %r)\n'
-                    ) % (c, b.real, b.imag,
-                         e.real, e.imag,
-                         a.real, a.imag)
-                         
-                self.rAlmostEqual(e.real, a.real,
-                               rel_err=rel_err, msg=error_message)
-                self.rAlmostEqual(e.imag, a.imag,
-                               rel_err=rel_err, msg=error_message)
+                assert (a[0].real - e.real) < rel_err
+                assert (a[0].imag - e.imag) < rel_err
 
     def test_subtract(self):
         from _numpypy import array, subtract
@@ -1089,6 +1028,58 @@
         if self.isWindows:
             skip('windows does not support c99 complex')
         import  _numpypy as np
+        from math import isnan, isinf, copysign
+        from sys import version_info
+
+        def rAlmostEqual(a, b, rel_err = 2e-15, abs_err = 5e-323, msg=''):
+            """Fail if the two floating-point numbers are not almost equal.
+
+            Determine whether floating-point values a and b are equal to within
+            a (small) rounding error.  The default values for rel_err and
+            abs_err are chosen to be suitable for platforms where a float is
+            represented by an IEEE 754 double.  They allow an error of between
+            9 and 19 ulps.
+            """
+
+            # special values testing
+            if isnan(a):
+                if isnan(b):
+                    return True,''
+                raise AssertionError(msg + '%r should be nan' % (b,))
+
+            if isinf(a):
+                if a == b:
+                    return True,''
+                raise AssertionError(msg + 'finite result where infinity expected: '+ \
+                                  'expected %r, got %r' % (a, b))
+
+            # if both a and b are zero, check whether they have the same sign
+            # (in theory there are examples where it would be legitimate for a
+            # and b to have opposite signs; in practice these hardly ever
+            # occur).
+            if not a and not b:
+                # only check it if we are running on top of CPython >= 2.6
+                if version_info >= (2, 6) and copysign(1., a) != copysign(1., b):
+                    raise AssertionError( msg + \
+                            'zero has wrong sign: expected %r, got %r' % (a, b))
+
+            # if a-b overflows, or b is infinite, return False.  Again, in
+            # theory there are examples where a is within a few ulps of the
+            # max representable float, and then b could legitimately be
+            # infinite.  In practice these examples are rare.
+            try:
+                absolute_error = abs(b-a)
+            except OverflowError:
+                pass
+            else:
+                # test passes if either the absolute error or the relative
+                # error is sufficiently small.  The defaults amount to an
+                # error of between 9 ulps and 19 ulps on an IEEE-754 compliant
+                # machine.
+                if absolute_error <= max(abs_err, rel_err * abs(a)):
+                    return True,''
+            raise AssertionError(msg + '%r and %r are not sufficiently close' % (a, b))
+
         def parse_testfile(fname):
             """Parse a file with test values
 
@@ -1153,7 +1144,7 @@
                          expected[0], expected[1],
                          actual[0], actual[1])
                          
-                self.rAlmostEqual(expected[0], actual[0],
+                rAlmostEqual(expected[0], actual[0],
                                abs_err=real_abs_err, msg=error_message)
-                self.rAlmostEqual(expected[1], actual[1],
+                rAlmostEqual(expected[1], actual[1],
                                    msg=error_message)
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -49,6 +49,19 @@
         )
     return dispatcher
 
+def complex_to_real_unary_op(func):
+    specialize.argtype(1)(func)
+    @functools.wraps(func)
+    def dispatcher(self, v):
+        return self.RealBoxType(
+            func(
+                self,
+                self.for_computation(self.unbox(v))
+            )
+        )
+    return dispatcher
+
+
 def raw_unary_op(func):
     specialize.argtype(1)(func)
     @functools.wraps(func)
@@ -193,6 +206,14 @@
         return v
 
     @simple_unary_op
+    def real(self, v):
+        return v
+
+    @simple_unary_op
+    def imag(self, v):
+        return 0
+
+    @simple_unary_op
     def abs(self, v):
         return abs(v)
 
@@ -1017,8 +1038,6 @@
                 return rfloat.NAN, rfloat.NAN
             return rfloat.INFINITY, rfloat.INFINITY
 
-
-
     @complex_unary_op
     def pos(self, v):
         return v
@@ -1031,9 +1050,17 @@
     def conj(self, v):
         return v[0], -v[1]
 
-    @raw_unary_op
+    @complex_to_real_unary_op
+    def real(self, v):
+        return self.RealBoxType(v.real)
+
+    @complex_to_real_unary_op
+    def imag(self, v):
+        return self.RealBoxType(v.imag)
+
+    @complex_to_real_unary_op
     def abs(self, v):
-        return rcomplex.c_abs(v[0], v[1])
+        return self.RealBoxType(rcomplex.c_abs(v[0], v[1]))
 
     @raw_unary_op
     def isnan(self, v):
@@ -1154,16 +1181,16 @@
             return v1
         elif self.isnan(v1):
             return v2
-        if v1 >= v2:
+        if self.ge(v1, v2):
             return v1
         return v2
 
     def fmin(self, v1, v2):
-        if math.isnan(v2):
+        if self.isnan(v2):
             return v1
-        elif math.isnan(v1):
+        elif self.isnan(v1):
             return v2
-        if v1 <= v2:
+        if self.le(v1, v2):
             return v1
         return v2
 
@@ -1334,7 +1361,7 @@
 
     @raw_unary_op
     def isnan(self, v):
-        return rfloat.isnan(v)
+        return rfloat.isnan(v[0]) or rfloat.isnan(v[1])
 
     @raw_unary_op
     def isinf(self, v):
@@ -1415,6 +1442,7 @@
     T = rffi.CHAR
     _COMPONENTS_T = rffi.FLOAT
     BoxType = interp_boxes.W_Complex64Box
+    RealBoxType = interp_boxes.W_Float32Box
 
 
 
@@ -1426,6 +1454,7 @@
     T = rffi.CHAR
     _COMPONENTS_T = rffi.DOUBLE
     BoxType = interp_boxes.W_Complex128Box
+    RealBoxType = interp_boxes.W_Float64Box
 
 
 NonNativeComplex128 = Complex128


More information about the pypy-commit mailing list