summaryrefslogtreecommitdiff
path: root/tests/run/ufunc.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'tests/run/ufunc.pyx')
-rw-r--r--tests/run/ufunc.pyx199
1 files changed, 199 insertions, 0 deletions
diff --git a/tests/run/ufunc.pyx b/tests/run/ufunc.pyx
new file mode 100644
index 000000000..a061035be
--- /dev/null
+++ b/tests/run/ufunc.pyx
@@ -0,0 +1,199 @@
+# mode: run
+# tag: numpy
+
+cimport cython
+
+import numpy as np
+
+# I'm making these arrays have slightly irregular strides deliberately
+int_arr_1d = np.arange(20, dtype=int)[::4]
+int_arr_2d = np.arange(500, dtype=int).reshape((50, -1))[5:8, 6:8]
+double_arr_1d = int_arr_1d.astype(np.double)
+double_arr_2d = int_arr_2d.astype(np.double)
+# Numpy has a cutoff at about 500 where it releases the GIL, so test some large arrays
+large_int_arr_1d = np.arange(1500, dtype=int)
+large_int_arr_2d = np.arange(1500*600, dtype=int).reshape((1500, -1))
+large_double_arr_1d = large_int_arr_1d.astype(np.double)
+large_double_arr_2d = large_int_arr_2d.astype(np.double)
+
+# it's fairly hard to test that nogil results in the GIL actually
+# being released unfortunately
+@cython.ufunc
+cdef double triple_it(long x) nogil:
+ """triple_it doc"""
+ return x*3.
+
+def test_triple_it():
+ """
+ Ufunc also generates a signature so just look at the end
+ >>> triple_it.__doc__.endswith('triple_it doc')
+ True
+ >>> triple_it(int_arr_1d)
+ array([ 0., 12., 24., 36., 48.])
+ >>> triple_it(int_arr_2d)
+ array([[168., 171.],
+ [198., 201.],
+ [228., 231.]])
+
+ Treat the large arrays just as a "don't crash" test
+ >>> _ = triple_it(large_int_arr_1d)
+ >>> _ = triple_it(large_int_arr_2d)
+ """
+
+@cython.ufunc
+cdef double to_the_power(double x, double y):
+ return x**y
+
+def test_to_the_power():
+ """
+ >>> np.allclose(to_the_power(double_arr_1d, 1.), double_arr_1d)
+ True
+ >>> np.allclose(to_the_power(1., double_arr_2d), np.ones_like(double_arr_2d))
+ True
+ >>> _ = to_the_power(large_double_arr_1d, -large_double_arr_1d)
+ >>> _ = to_the_power(large_double_arr_2d, -large_double_arr_2d)
+ """
+
+@cython.ufunc
+cdef object py_return_value(double x):
+ if x >= 0:
+ return x
+ # default returns None
+
+def test_py_return_value():
+ """
+ >>> py_return_value(5.)
+ 5.0
+ >>> py_return_value(double_arr_1d).dtype
+ dtype('O')
+ >>> py_return_value(-1.) # returns None
+ >>> _ = py_return_value(large_double_arr_1d)
+ """
+
+@cython.ufunc
+cdef double py_arg(object x):
+ return float(x)
+
+def test_py_arg():
+ """
+ >>> py_arg(np.array([1, "2.0", 3.0], dtype=object))
+ array([1., 2., 3.])
+ >>> _ = py_arg(np.array([1]*1200, dtype=object))
+ """
+
+@cython.ufunc
+cdef (double, long) multiple_return_values(long x):
+ return x*1.5, x*2
+
+@cython.ufunc
+cdef (double, long) multiple_return_values2(long x):
+ inefficient_tuple_intermediate = (x*1.5, x*2)
+ return inefficient_tuple_intermediate
+
+def test_multiple_return_values():
+ """
+ >>> multiple_return_values(int_arr_1d)
+ (array([ 0., 6., 12., 18., 24.]), array([ 0, 8, 16, 24, 32]))
+ >>> multiple_return_values2(int_arr_1d)
+ (array([ 0., 6., 12., 18., 24.]), array([ 0, 8, 16, 24, 32]))
+ """
+
+@cython.ufunc
+cdef cython.numeric plus_one(cython.numeric x):
+ return x+1
+
+def test_plus_one():
+ """
+ This generates all the fused combinations
+ >>> plus_one(int_arr_1d) # doctest: +ELLIPSIS
+ array([ 1, 5, 9, 13, 17]...)
+ >>> plus_one(double_arr_2d)
+ array([[57., 58.],
+ [67., 68.],
+ [77., 78.]])
+ >>> plus_one(1.j)
+ (1+1j)
+ """
+
+###### Test flow-control ######
+# An initial implementation of ufunc did some odd restructuring of the code to
+# bring the functions completely inline at the Cython level. These tests were to
+# test that "return" statements work. They're less needed now, but don't do any
+# harm
+
+@cython.ufunc
+cdef double return_stops_execution(double x):
+ return x
+ print "This should not happen"
+
+@cython.ufunc
+cdef double return_in_if(double x):
+ if x<0:
+ return -x
+ return x
+
+@cython.ufunc
+cdef double nested_loops(double x):
+ cdef double counter=0
+ while x>counter:
+ counter+=10.
+ for i in range(100):
+ if i>x:
+ return i
+ return x-counter
+
+def test_flow_control():
+ """
+ >>> np.allclose(return_stops_execution(double_arr_1d), double_arr_1d)
+ True
+ >>> return_in_if(-1.)
+ 1.0
+ >>> return_in_if(2.0)
+ 2.0
+ >>> nested_loops(5.5)
+ 6.0
+ >>> nested_loops(105.)
+ -5.0
+ """
+
+@cython.ufunc
+cdef double nested_function(double x):
+ def f(x):
+ return x*2
+ return f(x)
+
+def test_nested_function():
+ """
+ >>> np.allclose(nested_function(double_arr_1d), 2*double_arr_1d)
+ True
+ >>> nested_function(-1.)
+ -2.0
+ """
+
+@cython.ufunc
+cdef double can_throw(double x):
+ if x<0:
+ raise RuntimeError
+ return x
+
+def test_can_throw():
+ """
+ >>> arr = double_arr_1d.copy()
+ >>> arr[1] = -1.
+ >>> can_throw(arr)
+ Traceback (most recent call last):
+ ...
+ RuntimeError
+ >>> large_arr = large_double_arr_1d.copy()
+ >>> large_arr[-4] = -2.
+ >>> can_throw(large_arr)
+ Traceback (most recent call last):
+ ...
+ RuntimeError
+ >>> large_arr2d = large_double_arr_2d.copy()
+ >>> large_arr2d[100, 200] = -1.
+ >>> can_throw(large_arr2d)
+ Traceback (most recent call last):
+ ...
+ RuntimeError
+ """