Skip to content Skip to sidebar Skip to footer

Using Cython To Early Type Class Attributes

I'm writing a python class and I would like to accelerate the execution using cython early typing. I get the error 'Syntax error in C variable declaration' when I try to cython com

Solution 1:

What you want is to define an extension type. In particular your code should look like:

import numpy as np
cimport numpy as np

cdefclass MyClass:
    cdefdouble var1
    cdefnp.ndarray[double, ndim=1] Redges

    def__init__( self, np.ndarray[double, ndim=1] Redges ):
        self.Redges = Redges

Note that you cannot impose the type of instance attributes in a normal class, because python allow people to change them and their types. If you try to put a cdef at class level in a normal python class you'll receive a compiler error by Cython.


Compiling the above code raises the following error:

Error compiling Cython file:
------------------------------------------------------------                       
...                                                                                
import numpy as np                                                                 
cimport numpy as np                                                                

cdef classMyClass:                                                                
    cdef double var1                                                               
    cdef np.ndarray[double, ndim=1] Redges                                         
                                   ^                                               
------------------------------------------------------------                       

test_cython.pyx:6:36: Buffer types only allowed asfunction local variables

Now, this is not a syntax error. The syntax is fine. The problem is that you simply cannot have an instance attribute with np.ndarray as type. It is a limitation of cython. In fact if you comment the cdef np.ndarray[double, ndim=1] Redges line the file is compiled correctly:

Code:

import numpy as np
cimport numpy as np

cdefclass MyClass:
    cdefdouble var1
    #cdef np.ndarray[double, ndim=1] Redgesdef__init__( self, np.ndarray[double, ndim=1] Redges ):
        self.Redges = Redges

Output:

$cython test_cython.pyx 
$

Note: no output from cython which means the file was compiled succesfully.

this limitation is explained in the documentation I linked above, at the section Attributes:

Attributes of an extension type are stored directly in the object’s C struct. [omissis]

Note: You can only expose simple C types, such as ints, floats, and strings, for Python access. You can also expose Python-valued attributes.

The fact that you can only expose simple C data-types is because the attributes are members of the struct. Allowing a buffer like an np.ndarray would require to have variable size structs.

If you want an instance attribute of type np.ndarray the best you can do is to define an attribute with a generic type of object and assign the array to it:

import numpy as np
cimport numpy as np

cdefclass MyClass:
    cdefdouble var1
    cdefobject Redges

    def__init__( self, np.ndarray[double, ndim=1] Redges ):
        self.Redges = Redges

However now everytime you access self.Redges you lose the speed up of cython. If you access it many times you can assign it to a local variable with the correct type. Here's what I mean:

import numpy as np
cimport numpy as np

cdefclass MyClass:
    cdefdouble var1
    cdefobject Redges

    def__init__( self, np.ndarray[double, ndim=1] Redges ):
        self.Redges = Redges

    defdo_stuff(self):
        cdefnp.ndarray[double, ndim=1] ar
        ar = self.Redges
        ar[0] += 1return ar[0]

In this way inside the do_stuff function you can have all the speed of cython using ar.

Solution 2:

The answer of @bakuriu is quite good, I'd like to add how this can be done with keeping a memory view as a class member:

import numpy as np
cimport numpy as np

cdefclass MyClass:
    cdefpublic double var1
    cdefpublic np.float64_t[:] Redges

    def__init__( self, np.ndarray[double, ndim=1] Redges ):
        self.Redges = Redges

With that approach do_stuff gets simpler:

defdo_stuff(self):
    # With using buffer protocol, this just wraps the memory view# with numpy object without copying data
    np_redges = np.asarray(self.Redges)

    # Now you have np_redges, a numpy object. Even though, it's not a pure # C array, it allows calling numpy functions with all the power of MKL, e.g.:
    np.add(np_redges, 1.0, np_redges)

Post a Comment for "Using Cython To Early Type Class Attributes"