3

I've created a dtype for my np.ndarrays:

particle_t = np.dtype([
    ('position', float, 2),
    ('momentum', float, 2),
    ('velocity', float, 2),
    ('force', float, 2),
    ('charge', int, 1),
])

According to the official examples one can call:

def my_func(np.ndarray[dtype, dim] particles):

but when I try to compile:

def tester(np.ndarray[particle_t, ndim = 1] particles):

I get the Invalid type error. Another possibility of usage I've seen is with the memory view like int[:]. Trying def tester(particle_t[:] particles): results in: 'particle_t' is not a type identifier.

How can I fix this?

ead
  • 32,758
  • 6
  • 90
  • 153

1 Answers1

4

Obviously, particle_t is not a type but a Python-object as far as Cython is concerned.

It is similar to np.int32 being a Python-object and thus

def tester(np.ndarray[np.int32] particles):     #doesn't work!
       pass

won't work, you need to use the corresponding type, i.e. np.int32_t:

 def tester(np.ndarray[np.int32_t] particles):  #works!
      pass

But what is the corresponding type for particle_t? You need to create a packed struct, which would mirror your numpy-type. Here is a simplified version:

#Python code:
particle_t = np.dtype([
    ('position', np.float64, 2), #It is better to specify the number of bytes exactly!
    ('charge', np.int32, 1),  #otherwise you might be surprised...
])

and corresponding Cython code:

%%cython
import numpy as np
cimport numpy as np

cdef packed struct cy_particle_t:
    np.float64_t position_x[2]
    np.int32_t   charge

def tester(np.ndarray[cy_particle_t, ndim = 1] particles):
    print(particles[0])

Not only does it compile and load, it also works as advertised:

>>> t=np.zeros(2, dtype=particle_t)
>>> t[:]=42
>>> tester(t)
{'charge': 42, 'position_x': [42.0, 42.0]}
ead
  • 32,758
  • 6
  • 90
  • 153
  • By specifying the number of bytes (in the python code) do you mean working with np.float64 instead of just float? – Piotr Benedysiuk Jun 17 '18 at 12:55
  • 1
    Yes. At least to me `np.float64_t` is clearer than `np.float`. All above for integers: For example the size of `np.int_t` depends on the platform (Linux64 (8bytes) vs. Windows64 (4bytes)) and can be different from the size of `int` (8bytes vs. 4bytes on Linux64), because it maps to `long` and not `int` internally. – ead Jun 17 '18 at 13:14
  • I was getting some errors with ```long``` being passed down to functions even though I never defined anything to be a ```long```. Thanks allot, this fixes this issue. – Piotr Benedysiuk Jun 17 '18 at 13:52