3

I created a typed memoryview in cython and would like to multiply it by a scalar:

import numpy as np
import math
cimport numpy as np

def foo():
    N = 10
    cdef np.double_t [:, :] A = np.ones(shape=(N,N),dtype=np.double_)
    cdef int i,j
    cdef double pi = math.pi
    for i in range(N):
        for j in range(N):
            A[i,j] *= pi
    return A

def bar():
    N = 10
    cdef np.double_t [:, :] A = np.ones(shape=(N,N),dtype=np.double_)
    cdef double pi = math.pi
    A *= pi
    return A

Function foo() does this task but is not very convenient/readable.

The line A *= pi in function bar() does however not compile: Invalid operand types for '*' (double_t[:, :]; double).

Is there a way to perform such a broadcasting operation on a cython memoryview?

David
  • 33
  • 2

1 Answers1

4

No, memoryviews don't do this. A memoryview is literally just a way to access individual elements of an array quickly. It has no really concept of the mathematical operations that can be performed on the array.

In the case of your bar function, any attempt to type it is probably actually going to make it worse (i.e. it'll spend extra time checking the type, but ultimately the work is done in ordinary calls to Numpy function).

There's a number of (not 100% satisfactory) ways of getting a Numpy array from a memoryview:

  1. np.asarray(memview) - this should be done without copying (provided you aren't using the esoteric indirect memory layout). It might be worth adding an assertion to check that no copy was made though.

  2. memview.base - be slightly careful with this. If the memoryview is a result of slicing then .base will be the original unsliced object.

  3. Keep a parallel numpy array and memoryview variable:

     Anp = np.array(...)
     cdef double[:] Amview = Anp
    

    because the memoryview is a view of some memory, modifications to the array will be reflected in the memoryview and vice-versa. (Reassigning the array variable, e.g. Anp = something_else, won't be reflected though).


In summary, memoryviews are designed for one main job: being able to access individual elements quickly. If that's not what you're doing then you probably don't want to use a memoryview.

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Thanks for your suggestions! You are right, the best approach is probably going to be to use typed memviews for fast indexing and classic numpy functions otherwise. – David Feb 10 '21 at 11:03
  • Sorry, I don't understand. I'm having the same question, i.e. can we do numpy-ish boardcasting in cython with memoryview. Looks like @DavidW said no in this answer. So memoryview should only be used for fast indexing purposes, and numpy functions could be used for boardcasting inside cython, right? If so, then using numpy functions in cython would cause python overhead or not? – avocado Aug 26 '21 at 22:51
  • Also, if we use numpy functions (e.g. `np.sum`) on a memoryview, then this cdef function cannot be `nogil`, right? – avocado Aug 26 '21 at 23:03
  • 1
    @avocado Numpy functions would cause Python overhead and would require the GIL. This is the price of convenience. – DavidW Aug 27 '21 at 05:35