diff options
Diffstat (limited to 'tests/run/cpow.pyx')
-rw-r--r-- | tests/run/cpow.pyx | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/tests/run/cpow.pyx b/tests/run/cpow.pyx new file mode 100644 index 000000000..6f050337c --- /dev/null +++ b/tests/run/cpow.pyx @@ -0,0 +1,264 @@ +# mode: run +# tag: warnings + +from __future__ import print_function + +cimport cython +import sys + +if sys.version_info[0] > 2: + # The <object> path doesn't work in Py2 + __doc__ = """ + >>> pow_double_double(-4, 0.5, 1e-15) + soft double complex complex + """ + +def pow_double_double(double a, double b, delta): + """ + >>> pow_double_double(2, 2, 1e-15) + soft double complex float + >>> pow_double_double(4, 0.5, 1e-15) + soft double complex float + """ + c = a**b + # print out the Cython type, and the coerced type + print(cython.typeof(c), type(c).__name__) + object_c = (<object>a)**(<object>b) + assert abs((c/object_c) - 1) < delta + +@cython.cpow(True) +def pow_double_double_cpow(double a, double b, delta=None): + """ + >>> pow_double_double_cpow(2, 2, 1e-15) + double float + >>> pow_double_double_cpow(4, 0.5, 1e-15) + double float + >>> x = pow_double_double_cpow(-4, 0.5) + double float + >>> x == x # is nan + False + """ + c = a**b + # print out the Cython type, and the coerced type + print(cython.typeof(c), type(c).__name__) + if delta is not None: + object_c = (<object>a)**(<object>b) + assert abs((c/object_c) - 1) < delta + else: + return c + +cdef cfunc_taking_double(double x): + return x + +def pow_double_double_coerced_directly(double a, double b): + """ + >>> pow_double_double_coerced_directly(2, 2) + 8.0 + >>> x = pow_double_double_coerced_directly(-2, 0.5) + >>> x == x # nan + False + """ + # Because we're assigning directly to a double assume 'cpow' + # but warn. + cdef double c = a**b + return cfunc_taking_double(a**b) + c + +def pow_double_int(double a, int b): + """ + # a few variations of 'double**int'. In all cases + # Cython should realise that the result can't be complex + # and avoid going through the soft complex type + >>> pow_double_int(5, 2) + double + double + double + double + double + """ + c1 = a**b + c2 = a**2.0 + c3 = a**-2.0 + c4 = a**5 + c5 = a**-5 + print(cython.typeof(c1)) + print(cython.typeof(c2)) + print(cython.typeof(c3)) + print(cython.typeof(c4)) + print(cython.typeof(c5)) + +def soft_complex_coerced_to_double(double a, double b): + """ + >>> soft_complex_coerced_to_double(2, 2) + 4.0 + >>> soft_complex_coerced_to_double(-2, 0.25) + Traceback (most recent call last): + ... + TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number). + """ + c = a**b + assert cython.typeof(c) == "soft double complex" + cdef double d = c # will raise if complex + return d + +def soft_complex_coerced_to_complex(double a, double b): + """ + >>> soft_complex_coerced_to_complex(2, 2) + (4+0j) + >>> x = soft_complex_coerced_to_complex(-1, 0.5) + >>> abs(x.real) < 1e-15 + True + >>> abs(x.imag - 1) < 1e-15 + True + """ + # This is always fine, but just check it works + c = a**b + assert cython.typeof(c) == "soft double complex" + cdef double complex d = c + return d + +def soft_complex_type_inference_1(double a, double b, pick): + """ + >>> soft_complex_type_inference_1(2, 1, False) + soft double complex 2.0 + >>> soft_complex_type_inference_1(2, 3, True) + soft double complex 4.0 + """ + # double and soft complex should infer to soft-complex + if pick: + c = a**2 + else: + c = a**b + print(cython.typeof(c), c) + +def soft_complex_type_inference_2(double a, double b, expected): + """ + >>> soft_complex_type_inference_2(2, 1, 1.0) + soft double complex + >>> soft_complex_type_inference_2(2, 3, 7.0) + soft double complex + """ + # double and soft complex should infer to soft-complex + c = a**b + c -= 1 + print(cython.typeof(c)) + delta = abs(c/expected - 1) + assert delta < 1e-15, delta + +def pow_int_int(int a, int b): + """ + >>> pow_int_int(2, 2) + double 4.0 + >>> pow_int_int(2, -2) + double 0.25 + """ + c = a**b + print(cython.typeof(c), c) + +@cython.cpow(True) +def pow_int_int_cpow(int a, int b): + """ + >>> pow_int_int_cpow(2, 2) + int 4 + >>> pow_int_int_cpow(2, -2) + int 0 + """ + c = a**b + print(cython.typeof(c), c) + +cdef cfunc_taking_int(int x): + return x + +def pow_int_int_coerced_directly(int a, int b): + """ + Generates two warnings about using cpow. + The actual behaviour isn't too easy to distinguish + without inspecting the c code though. + >>> pow_int_int_coerced_directly(2, 2) + 8 + """ + cdef int c = a**b + return cfunc_taking_int(a**b) + c + +def pow_int_int_non_negative(int a, unsigned int b): + """ + A couple of combinations of non-negative values for the + exponent, which lets us fall back to int as a return type + >>> pow_int_int_non_negative(5, 3) + unsigned int + long + """ + c1 = a**b + c2 = a**5 + print(cython.typeof(c1)) + print(cython.typeof(c2)) + + +ctypedef double f64 + +def pythagoras_with_typedef(double a, double b): + # see https://github.com/cython/cython/issues/5203 + """ + >>> rc = pythagoras_with_typedef(2.0, 2.0) + >>> pyresult = 1.0 / (2 * 2.0 ** 2) ** 0.5 + >>> pyresult - 0.001 < rc < pyresult + 0.001 or (rc, pyresult) + True + """ + cdef f64 result = a * a + b * b + result = 1.0 / result ** 0.5 + return result + + +@cython.cpow(False) +def power_coercion_in_nogil_1(double a, double b): + """ + >>> power_coercion_in_nogil_1(2., 2.) + 4.0 + >>> power_coercion_in_nogil_1(-1., 0.5) + Traceback (most recent call last): + ... + TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number). + """ + cdef double c + with nogil: + c = a**b + return c + + +cdef double nogil_fun(double x) nogil: + return x + +def power_coercion_in_nogil_2(double a, double b): + """ + >>> power_coercion_in_nogil_2(2., 2.) + 4.0 + >>> power_coercion_in_nogil_2(-1., 0.5) + Traceback (most recent call last): + ... + TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number). + """ + c = a**b + with nogil: + d = nogil_fun(c) + return d + + +def power_coercion_in_nogil_3(double a, double b, double c): + """ + >>> power_coercion_in_nogil_3(2., 2., 1.0) + 0.25 + >>> power_coercion_in_nogil_3(-1., 0.5, 1.0) + Traceback (most recent call last): + ... + TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number). + """ + with nogil: + c /= a**b + return c + + +_WARNINGS = """ +63:21: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a a non-complex C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly. +64:32: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a a non-complex C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly. +179:18: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a an integer C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly. +180:29: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a an integer C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly. +""" |