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