Creating Numpy ufuncs¶
This page uses two different syntax variants:
cdefsyntax, which was designed to make type declarations concise and easily readable from a C/C++ perspective.
Pure Python syntax which allows static Cython type declarations in pure Python code, following PEP-484 type hints and PEP 526 variable annotations.
To make use of C data types in Python syntax, you need to import the special
cythonmodule in the Python module that you want to compile, e.g.
If you use the pure Python syntax we strongly recommend you use a recent Cython 3 release, since significant improvements have been made here compared to the 0.29.x releases.
Numpy supports a special type of function called a ufunc . These support array broadcasting (i.e. the ability to handle arguments with any number of dimensions), alongside other useful features.
Cython can generate a ufunc from a Cython C function by tagging it with the
decorator. The input and output argument types should be scalar variables (“generic ufuncs” are
not yet supported) and should either by Python objects or simple numeric types. The body
of such a function is inserted into an efficient, compiled loop.
import cython @cython.cfunc @cython.ufunc def add_one(x: cython.double) -> cython.double: # of course, this simple operation can already by done efficiently in Numpy! return x+1
cimport cython @cython.ufunc cdef double add_one(double x): # of course, this simple operation can already by done efficiently in Numpy! return x+1
You can have as many arguments to your function as you like. If you want to have multiple output arguments then you can use the ctuple syntax:
import cython @cython.cfunc @cython.ufunc def add_one_add_two(x: cython.int) -> (cython.int, cython.int): return x+1, x+2
cimport cython @cython.ufunc cdef (int, int) add_one_add_two(int x): return x+1, x+2
If you want to accept multiple different argument types then you can use Fused Types (Templates):
import cython @cython.cfunc @cython.ufunc def generic_add_one(x: cython.numeric) -> cython.numeric: return x+1
cimport cython @cython.ufunc cdef cython.numeric generic_add_one(cython.numeric x): return x+1
Finally, if you declare the
@cfunc function as
nogil then Cython will release the
GIL once in the generated ufunc. This is a slight difference
from the general behaviour of
nogil functions (they generally do not automatically
release the GIL, but instead can be run without the GIL).
This feature relies on Numpy. Therefore if you create a ufunc in Cython, you must have the Numpy headers available when you build the generated C code, and users of your module must have Numpy installed when they run it.