Author: mattip <matti.pi...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit