🤝 Like the tool? Help making it better! Your donation helps! 🤝

# Creating Numpy ufuncs

Note

This page uses two different syntax variants:

- Cython specific `cdef` syntax, 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 `cython` module in the Python module that you want to compile, e.g.

      import cython

  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 `@cython.ufunc` 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.ufunc
    @cython.cfunc
    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.ufunc
    @cython.cfunc
    def add_one_add_two(x: cython.int) -> tuple[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.ufunc
    @cython.cfunc
    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 `cdef`/`@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.