[pypy-commit] pypy numpypy-complex2: copy complex_testcases to test_ufuncs

mattip noreply at buildbot.pypy.org
Fri Aug 31 17:45:19 CEST 2012


Author: mattip <matti.picus at gmail.com>
Branch: numpypy-complex2
Changeset: r57063:9ab2857e5445
Date: 2012-08-31 15:34 +0300
http://bitbucket.org/pypy/pypy/changeset/9ab2857e5445/

Log:	copy complex_testcases to test_ufuncs

diff --git a/pypy/rlib/test/rcomplex_testcases.txt b/pypy/module/micronumpy/test/complex_testcases.txt
copy from pypy/rlib/test/rcomplex_testcases.txt
copy to pypy/module/micronumpy/test/complex_testcases.txt
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,6 +2,12 @@
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
 
 class AppTestUfuncs(BaseNumpyAppTest):
+    def setup_class(cls):
+        import os
+        BaseNumpyAppTest.setup_class.im_func(cls)
+        fname = os.path.join(os.path.dirname(__file__), 'complex_testcases.txt')
+        cls.w_testcases = cls.space.wrap(fname)
+
     def test_ufunc_instance(self):
         from _numpypy import add, ufunc
 
@@ -856,7 +862,8 @@
 
     def test_complex(self):
         from _numpypy import (complex128, complex64, add,
-            subtract as sub, multiply, divide, negative, abs, fmod)
+            subtract as sub, multiply, divide, negative, abs, fmod, 
+            reciprocal)
         from _numpypy import (equal, not_equal, greater, greater_equal, less,
                 less_equal)
 
@@ -916,7 +923,147 @@
             assert repr(abs(inf_c)) == 'inf'
             assert repr(abs(n)) == 'nan'
 
+            assert False, 'untested: copysign, reciprocal, sign, floor_div, ' + \
+                     'signbit, fabs, fmax, fmin, floor, ceil, trunc, ' + \
+                     'exp2, expm1, isnan, isinf, isneginf, isposinf, ' + \
+                     'isfinite, radians, degrees, log2, log10, log1p, ' + \
+                     'logaddexp, npy_log2_1p, logaddexp2'
 
     def test_complex_math(self):
-        # from _numpypy import 
-        pass
+        import  _numpypy as np
+        from math import isnan, isinf, copysign
+        from sys import version_info
+        testcases = self.testcases
+        def parse_testfile(fname):
+            """Parse a file with test values
+
+            Empty lines or lines starting with -- are ignored
+            yields id, fn, arg_real, arg_imag, exp_real, exp_imag
+            """
+            with open(fname) as fp:
+                for line in fp:
+                    # skip comment lines and blank lines
+                    if line.startswith('--') or not line.strip():
+                        continue
+
+                    lhs, rhs = line.split('->')
+                    id, fn, arg_real, arg_imag = lhs.split()
+                    rhs_pieces = rhs.split()
+                    exp_real, exp_imag = rhs_pieces[0], rhs_pieces[1]
+                    flags = rhs_pieces[2:]
+
+                    yield (id, fn,
+                           float(arg_real), float(arg_imag),
+                           float(exp_real), float(exp_imag),
+                           flags
+                          )
+        def rAssertAlmostEqual(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
+                raise AssertionError(msg + '%r should be nan' % (b,))
+
+            if isinf(a):
+                if a == b:
+                    return
+                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
+            raise AssertionError(msg + '%r and %r are not sufficiently close' % (a, b))
+        tested_funcs=[]
+        for complex_, abs_err in ((np.complex128, 5e-323), (np.complex64, 5e-32)):
+            for id, fn, ar, ai, er, ei, flags in parse_testfile(testcases):
+                arg = complex_(complex(ar, ai))
+                expected = (er, ei)
+                if fn.startswith('acos'):
+                    fn = 'arc' + fn[1:]
+                elif fn.startswith('asin'):
+                    fn = 'arc' + fn[1:]
+                elif fn.startswith('atan'):
+                    fn = 'arc' + fn[1:]
+                function = getattr(np, fn)
+                #
+                #if 'divide-by-zero' in flags or 'invalid' in flags:
+                #    try:
+                #        _actual = function(arg)
+                #    except ValueError:
+                #        continue
+                #    else:
+                #        raise AssertionError('ValueError not raised in test '
+                #                '%s: %s(complex(%r, %r))' % (id, fn, ar, ai))
+                #if 'overflow' in flags:
+                #    try:
+                #        _actual = function(arg)
+                #    except OverflowError:
+                #        continue
+                #    else:
+                #        raise AssertionError('OverflowError not raised in test '
+                #               '%s: %s(complex(%r, %r))' % (id, fn, ar, ai))
+                _actual = function(arg)
+                actual = (_actual.real, _actual.imag)
+
+                if 'ignore-real-sign' in flags:
+                    actual = (abs(actual[0]), actual[1])
+                    expected = (abs(expected[0]), expected[1])
+                if 'ignore-imag-sign' in flags:
+                    actual = (actual[0], abs(actual[1]))
+                    expected = (expected[0], abs(expected[1]))
+
+                # for the real part of the log function, we allow an
+                # absolute error of up to 2e-15.
+                if fn in ('log', 'log10'):
+                    real_abs_err = 2e-15
+                else:
+                    real_abs_err = abs_err
+
+                error_message = (
+                    '%s: %s(complex(%r, %r))\n'
+                    'Expected: complex(%r, %r)\n'
+                    'Received: complex(%r, %r)\n'
+                    ) % (id, fn, ar, ai,
+                         expected[0], expected[1],
+                         actual[0], actual[1])
+                         
+                if not function in tested_funcs:        
+                    print 'fuction',function
+                    tested_funcs.append(function)
+                rAssertAlmostEqual(expected[0], actual[0],
+                                   abs_err=real_abs_err,
+                                   msg=error_message)
+                rAssertAlmostEqual(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
@@ -5,8 +5,8 @@
 from pypy.interpreter.error import OperationError
 from pypy.module.micronumpy import interp_boxes
 from pypy.objspace.std.floatobject import float2string
-from pypy.objspace.std.complexobject import W_ComplexObject, str_format
-from pypy.rlib import rfloat, libffi, clibffi, rcomplex
+from pypy.objspace.std.complexobject import str_format
+from pypy.rlib import rfloat, clibffi, rcomplex
 from pypy.rlib.rawstorage import (alloc_raw_storage, raw_storage_setitem,
                                   raw_storage_getitem)
 from pypy.rlib.objectmodel import specialize, we_are_translated
@@ -15,7 +15,6 @@
 from pypy.rlib.rstruct.runpack import runpack
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.rlib import jit
-from pypy.module import cmath
 
 
 degToRad = math.pi / 180.0
@@ -39,12 +38,17 @@
     specialize.argtype(1)(func)
     @functools.wraps(func)
     def dispatcher(self, v):
-        return self.box_complex(
-            *func(
-                self,
-                self.for_computation(self.unbox(v))
+        try:
+            return self.box_complex(
+                *func(
+                    self,
+                    self.for_computation(self.unbox(v))
+                )
             )
-        )
+        except:
+            import sys
+            print >> sys.stderr, "Could not call",func
+            raise
     return dispatcher
 
 def raw_unary_op(func):
@@ -1113,22 +1117,14 @@
         except ZeroDivisionError:
             return rfloat.NAN, 0
 
-    #complex mod does not exist
+    #complex mod does not exist in numpy
     #@simple_binary_op
     #def mod(self, v1, v2):
     #    return math.fmod(v1, v2)
 
-    @simple_binary_op
+    @complex_binary_op
     def pow(self, v1, v2):
-        try:
-            return math.pow(v1, v2)
-        except ValueError:
-            return rfloat.NAN
-        except OverflowError:
-            if math.modf(v2)[0] == 0 and math.modf(v2 / 2)[0] != 0:
-                # Odd integer powers result in the same sign as the base
-                return rfloat.copysign(rfloat.INFINITY, v1)
-            return rfloat.INFINITY
+        return rcomplex.c_pow(v1, v2)
 
     @simple_binary_op
     def copysign(self, v1, v2):
@@ -1144,9 +1140,9 @@
     def signbit(self, v):
         return rfloat.copysign(1.0, v) < 0.0
 
-    @simple_unary_op
+    @complex_unary_op
     def fabs(self, v):
-        return math.fabs(v)
+        return rcomplex.abs(*v)
 
     @simple_binary_op
     def fmax(self, v1, v2):
@@ -1164,17 +1160,17 @@
             return v2
         return min(v1, v2)
 
-    @simple_binary_op
-    def fmod(self, v1, v2):
-        try:
-            return math.fmod(v1, v2)
-        except ValueError:
-            return rfloat.NAN
+    #@simple_binary_op
+    #def fmod(self, v1, v2):
+    #    try:
+    #        return math.fmod(v1, v2)
+    #    except ValueError:
+    #        return rfloat.NAN
 
     @simple_unary_op
     def reciprocal(self, v):
-        if v == 0.0:
-            return rfloat.copysign(rfloat.INFINITY, v)
+        if abs(v) == 0.0:
+            return self.copysign(rfloat.INFINITY, v)
         return 1.0 / v
 
     @simple_unary_op
@@ -1213,74 +1209,70 @@
         except OverflowError:
             return rfloat.INFINITY
 
-    @simple_unary_op
+    @complex_unary_op
     def sin(self, v):
-        return math.sin(v)
+        return rcomplex.c_sin(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def cos(self, v):
-        return math.cos(v)
+        return rcomplex.c_cos(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def tan(self, v):
-        return math.tan(v)
+        return rcomplex.c_tan(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def arcsin(self, v):
-        if not -1.0 <= v <= 1.0:
-            return rfloat.NAN
-        return math.asin(v)
+        return rcomplex.c_asin(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def arccos(self, v):
-        if not -1.0 <= v <= 1.0:
-            return rfloat.NAN
-        return math.acos(v)
+        return rcomplex.c_acos(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def arctan(self, v):
-        return math.atan(v)
+        if v[0]==0 and (v[1]==1 or v[1] == -1):
+            #This is the place to print a "runtime warning"
+            return rfloat.NAN, math.copysign(rfloat.INFINITY, v[1])
+        return rcomplex.c_atan(*v)
 
-    @simple_binary_op
+    @complex_binary_op
     def arctan2(self, v1, v2):
-        return math.atan2(v1, v2)
+        return rcomplex.c_atan2(v1, v2)
 
-    @simple_unary_op
+    @complex_unary_op
     def sinh(self, v):
-        return math.sinh(v)
+        return rcomplex.c_sinh(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def cosh(self, v):
-        return math.cosh(v)
+        return rcomplex.c_cosh(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def tanh(self, v):
-        return math.tanh(v)
+        return rcomplex.c_tanh(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def arcsinh(self, v):
-        return math.asinh(v)
+        return rcomplex.c_asinh(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def arccosh(self, v):
-        if v < 1.0:
-            return rfloat.NAN
-        return math.acosh(v)
+        return rcomplex.c_acosh(*v)
 
-    @simple_unary_op
+    @complex_unary_op
     def arctanh(self, v):
-        if v == 1.0 or v == -1.0:
-            return math.copysign(rfloat.INFINITY, v)
+        if v[1] == 0 and (v[0] == 1.0 or v[0] == -1.0):
+            return math.copysign(rfloat.INFINITY, v[0]), 0.
         if not -1.0 < v < 1.0:
-            return rfloat.NAN
-        return math.atanh(v)
+            return rfloat.NAN, 0.
+        return rcomplex.c_atanh(*v)
+            
+            
 
-    @simple_unary_op
+    @complex_unary_op
     def sqrt(self, v):
-        try:
-            return math.sqrt(v)
-        except ValueError:
-            return rfloat.NAN
+        return rcomplex.c_sqrt(*v)
 
     @simple_unary_op
     def square(self, v):
@@ -1315,27 +1307,13 @@
     def degrees(self, v):
         return v / degToRad
 
-    @simple_unary_op
+    @complex_unary_op
     def log(self, v):
-        try:
-            return math.log(v)
-        except ValueError:
-            if v == 0.0:
-                # CPython raises ValueError here, so we have to check
-                # the value to find the correct numpy return value
-                return -rfloat.INFINITY
-            return rfloat.NAN
+        return rcomplex.c_log(v)
 
     @simple_unary_op
     def log2(self, v):
-        try:
-            return math.log(v) / log2
-        except ValueError:
-            if v == 0.0:
-                # CPython raises ValueError here, so we have to check
-                # the value to find the correct numpy return value
-                return -rfloat.INFINITY
-            return rfloat.NAN
+        return self.log(v) / log2
 
     @simple_unary_op
     def log10(self, v):


More information about the pypy-commit mailing list