Search code examples
pythoncythonnumpy-ndarray

What does `mode='c'` do in numpy.ndarray[..., mode='c'] do?


I am modifying cython code that makes plots of complex-valued functions. This code includes something equivalent to the following:

cimport numpy as cnumpy

def do_something():
    # Buffer types only allowed as function local variables, so put
    # this inside a function.
    cdef cnumpy.ndarray[cnumpy.float_t, ndim=3, mode='c'] rgb
    pass

What does the mode='c' kwarg do? I don't see this as an option on the numpy.ndarray documentation (or anywhere else in the numpy documentation), or mentioned in the cython for numpy users documentation/tutorial, or mentioned in cython's working with numpy documentation/tutorial.

I am not fluent in cython, but I do know python and C. I see from other questions here that this is not a rare argument, but I can't find documentation or explanation for what this does.


Solution

  • Argument mode maps to Python's buffer protocol's flags:

    • "c" means PyBUF_C_CONTIGUOUS
    • "fortran" means PyBUF_F_CONTIGUOUS
    • "strided" means PyBUF_STRIDES

    there is also "full" which maps to PyBUF_INDIRECT and thus doesn't make sense for numpy-arrays.

    Knowing, that the memory layout/mode is "c" or "fortran" would lead to generation of a more performant code, but then only numpy arrays with this layout (flags of the array must be either C_CONTIGUOUS=true for mode="c" or F_CONTIGUOUS=true for mode="fortran") would be accepted.

    On the other hand mode="strided" would accept any numpy ndarray, but the generated C-code would be less performant.


    The gory details:

    To access the content of a numpy array Cython uses Python's buffer protocol. Whenever there is a np.ndarray in the Cython code, there will be a corresponding __Pyx_Buffer variable in the C-code, for generation of which the class BufferEntry is responsible.

    To get the buffer from the array object, the C-function __Pyx_GetBufferAndValidate will be emitted. The mode-argument will be translated into the buffer-protocol flags used by the above function in BufferEntry.get_flags:

    def get_flags(buffer_aux, buffer_type):
        flags = 'PyBUF_FORMAT'
        mode = buffer_type.mode
        if mode == 'full':
            flags += '| PyBUF_INDIRECT'
        elif mode == 'strided':
            flags += '| PyBUF_STRIDES'
        elif mode == 'c':
            flags += '| PyBUF_C_CONTIGUOUS'
        elif mode == 'fortran':
            flags += '| PyBUF_F_CONTIGUOUS'
        else:
            assert False
        if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE"
        return flags