[pypy-commit] pypy numpypy-complex2: refactor 'close enough' check, fixes to make numpypy match rcomplex results

mattip noreply at buildbot.pypy.org
Thu Sep 6 21:55:52 CEST 2012


Author: mattip <matti.picus at gmail.com>
Branch: numpypy-complex2
Changeset: r57193:990dd3fa7779
Date: 2012-09-05 23:22 +0300
http://bitbucket.org/pypy/pypy/changeset/990dd3fa7779/

Log:	refactor 'close enough' check, fixes to make numpypy match rcomplex
	results

diff --git a/pypy/module/micronumpy/test/complex_testcases.txt b/pypy/module/micronumpy/test/complex_testcases.txt
--- a/pypy/module/micronumpy/test/complex_testcases.txt
+++ b/pypy/module/micronumpy/test/complex_testcases.txt
@@ -1004,7 +1004,7 @@
 atan1004 atan -inf 2.2999999999999998 -> -1.5707963267948966 0.0
 atan1005 atan nan 2.2999999999999998 -> nan nan
 atan1006 atan -0.0 inf -> -1.5707963267948966 0.0
-infn100 7atan -2.2999999999999998 inf -> -1.5707963267948966 0.0
+infn1007 atan -2.2999999999999998 inf -> -1.5707963267948966 0.0
 atan1008 atan -inf inf -> -1.5707963267948966 0.0
 atan1009 atan nan inf -> nan 0.0
 atan1010 atan -0.0 nan -> nan nan
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
@@ -1,12 +1,75 @@
 
 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,''
+        return False, msg + '%r should be nan' % (b,)
+
+    if isinf(a):
+        if a == b:
+            return True,''
+        return False, 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):
+            return False, 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,''
+    return False, msg + '%r and %r are not sufficiently close' % (a, b)
+
 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 cls_rAlmostEqual(self, *args, **kwargs):
+            return rAlmostEqual(*args, **kwargs)
+        cls.w_rAlmostEqual = cls.space.wrap(cls_rAlmostEqual)
+        if 1 or option.runappdirect:
+            #Continue test, fail after all cases are tested
+            cls.w_collect_all_failures = cls.space.wrap(True)
+        else:
+            #Fail at first subtest case failure
+            cls.w_collect_all_failures = cls.space.wrap(False)
+
 
     def test_ufunc_instance(self):
         from _numpypy import add, ufunc
@@ -120,6 +183,7 @@
 
     def test_true_divide(self):
         from _numpypy import array, true_divide
+        import math
 
         a = array([0, 1, 2, 3, 4, 1, -1])
         b = array([4, 4, 4, 4, 4, 0,  0])
@@ -931,8 +995,6 @@
 
     def test_complex_math(self):
         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
@@ -957,61 +1019,8 @@
                            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,))
-                print(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))
-                print(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))
-                    print(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))
-            print(msg + '%r and %r are not sufficiently close' % (a, b))
         tested_funcs=[]
+        fail_at_end = False
         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))
@@ -1051,8 +1060,22 @@
                 if not function in tested_funcs:        
                     print 'fuction',function
                     tested_funcs.append(function)
-                rAssertAlmostEqual(expected[0], actual[0],
-                                   abs_err=real_abs_err,
+                success,msg = self.rAlmostEqual(expected[0], actual[0],
+                               abs_err=real_abs_err, msg=error_message)
+                if not success:
+                    if self.collect_all_failures:
+                        print msg
+                        fail_at_end = True
+                    else:
+                        raise AssertionError(msg)
+                success,msg = self.rAlmostEqual(expected[1], actual[1],
                                    msg=error_message)
-                rAssertAlmostEqual(expected[1], actual[1],
-                                   msg=error_message)
+                if not success:
+                    if self.collect_all_failures:
+                        print msg
+                        fail_at_end = True
+                    else:
+                        raise AssertionError(msg)
+                    
+        if fail_at_end:
+            assert False,'at least one test failed, see stdout'
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
@@ -21,6 +21,8 @@
 log2 = math.log(2)
 log2e = 1. / log2
 
+def isfinite(d):
+    return not math.isinf(d) and not math.isnan(d)
 
 def simple_unary_op(func):
     specialize.argtype(1)(func)
@@ -1191,10 +1193,18 @@
     @complex_unary_op
     def exp(self, v):
         if math.isinf(v[1]):
-            return rfloat.NAN, rfloat.NAN
+            if math.isinf(v[0]):
+                if v[0]<0:
+                    return 0., 0.
+                return rfloat.INFINITY, rfloat.NAN
+            elif (isfinite(v[0]) or \
+                                 (math.isinf(v[0]) and v[0] > 0)):
+                return rfloat.NAN, rfloat.NAN
         try:
             return rcomplex.c_exp(*v)
         except OverflowError:
+            if v[1]==0:
+                return rfloat.INFINITY, 0.0
             return rfloat.INFINITY, rfloat.NAN
 
     def exp2(self, v):
@@ -1212,14 +1222,26 @@
 
     @complex_unary_op
     def sin(self, v):
+        if math.isinf(v[0]):
+            if isfinite(v[1]):
+                return rfloat.NAN, rfloat.INFINITY
+            elif not math.isnan(v[1]):
+                return rfloat.INFINITY, rfloat.NAN
         return rcomplex.c_sin(*v)
 
     @complex_unary_op
     def cos(self, v):
+        if math.isinf(v[0]):
+            if isfinite(v[1]):
+                return rfloat.NAN, 0.0
+            elif not math.isnan(v[1]):
+                return rfloat.INFINITY, rfloat.NAN
         return rcomplex.c_cos(*v)
 
     @complex_unary_op
     def tan(self, v):
+        if isinf(v[0]) and isfinite(v[1]):
+            return rfloat.NAN, rfloat.NAN
         return rcomplex.c_tan(*v)
 
     @complex_unary_op
@@ -1243,14 +1265,30 @@
 
     @complex_unary_op
     def sinh(self, v):
+        if math.isinf(v[1]):
+            if isfinite(v[0]):
+                if v[0] == 0.0:
+                    return 0.0, rfloat.NAN
+                return rfloat.NAN, rfloat.NAN
+            elif not math.isnan(v[0]):
+                return rfloat.INFINITY, rfloat.NAN
         return rcomplex.c_sinh(*v)
 
     @complex_unary_op
     def cosh(self, v):
+        if math.isinf(v[1]):
+            if isfinite(v[0]):
+                if v[0] == 0.0:
+                    return rfloat.NAN, 0.0
+                return rfloat.NAN, rfloat.NAN
+            elif not math.isnan(v[0]):
+                return rfloat.INFINITY, rfloat.NAN
         return rcomplex.c_cosh(*v)
 
     @complex_unary_op
     def tanh(self, v):
+        if math.isinf(v[1]) and isfinite(v[0]):
+            return rfloat.NAN, rfloat.NAN
         return rcomplex.c_tanh(*v)
 
     @complex_unary_op


More information about the pypy-commit mailing list