Reputation: 95
I have some code where I (am trying to) do roughly the following:
ctypedef fused Raster_t:
numpy.uint8_t
numpy.uint16_t
cdef class MyClass:
# shape is (rows, cols, channels)
cdef Raster_t[:,:,:] the_raster
def __init__(self, raster):
self.the_raster = raster
self.dtype = raster.dtype
def do_some_work(self):
cdef Raster_t[:,:,:] out_raster_memview
out_raster = numpy.empty((some_rows, some_cols, some_channels), dtype=self.dtype)
out_raster_memview = out_raster
#do cool stuff with values from self.the_raster
#and write values of the same type into out_raster_memview
#Most basic usage would be something like:
out_raster_memview[0,0,0] = self.the_raster[0,0,0]
But I can't even get something like this to compile. I've tried adding a dummy variable to the do_some_work function like:
def do_some_work(self):
self._do_some_work(0)
def _do_some_work(self, Raster_t dummy)
to try to get Cython to accept the thing as suggested here: Cython caching with fused type
I've tried moving the local cdef into a class member, cdef/un-cdefing things, and a bazillion little tweaks but all I get are vague compile errors like
(tree fragment):16:27: Syntax Error in C variable definition
Or things like
AttributeError: 'MemoryViewSliceType' object has no attribute 'dtype_name'
like: https://github.com/cython/cython/issues/1605
But adding 'const' doesn't seem to help either.
So I guess the question: How do I get the fused dtype to evaluate at runtime on an input numpy array?
################################################## Edit1:
Thanks to yut23, I've converted the class-attribute part of this problem into a local problem like the following:
ctypedef fused Raster_t
numpy.uint8_t
numpy.uint16_t
cdef class MyClass:
cdef object the_raster
def __init__(self, raster):
self.the_raster = raster
self.dtype = raster.dtype
def do_some_work(self):
cdef Raster_t[:,:,:] out_raster_memview
cdef Raster_t[:,:,:] in_raster_memview
out_raster = numpy.empty((rows, cols, channels), dtype=self.dtype)
out_raster_memview = out_raster
in_raster_memview = self.the_raster
Now the problem becomes:
out_raster_memview = out_raster
^
------------------------------------------
myfile:lineno:Cannot coerce to a type that is not specialized
How does one specialize the 'out_raster'? 'out_raster' is a python object/numpy array which I thought has full runtime-available typing info. It seems like this code should fail if I used the wrong dtype at runtime (maybe not crash but produce other bugs due to wrong buffer length/striding/etc), like if I used numpy.int64_t which isn't included in my 'Raster_t'. But I'm confused as to why this doesn't work if I provide a uint8 or uint16 array?
####################################### Edit2:
My guess is (Edit3: My guess was slightly wrong - perhaps the more accurate information would be from DavidW's comment - "fused types need to be deducible from the function arguments") that Cython cannot handle the actual type checking between a fused type and the actual python/numpy array, so I reorganized the code to look like so:
def do_some_work(self):
cdef numpy.uint8_t[:,:,:] raster_memview_uint8
cdef numpy.uint8_t[:,:,:] out_raster_memview_uint8
cdef numpy.uint16_t[:,:,:] raster_memview_uint16
cdef numpy.uint16_t[:,:,:] out_raster_memview_uint16
if self.dtype == numpy.uint8:
self._do_some_work(raster_memview_uint8, out_raster_memview_uint8)
if self.dtype == numpy.uint16:
self._do_some_work(raster_memview_uint16, out_raster_memview_uint16)
def _do_some_work(self,
Raster_t[:,:,:] out_raster_memview,
Raster_t[:,:,:] raster_memview):
# whatever complex stuff I was going to do in the body
I think this mostly solves the problem, but this still seems like a workaround with what seems like unnecessary code duplication still. On the bright side, at least I don't have to duplicate the body of "_do_some_work()", which is the real hard part and its just some boilerplate for every new type in the selection part.
I don't know the right protocol, but if the StackOverflow gurus dont mind I'd like to leave this open maybe for some Cython guru to come along and tell me if there is a better/cleaner way? This seems like maybe an 80% solution.
Upvotes: 1
Views: 87
Reputation: 3064
As per the fused types docs, fused types aren't supported as attributes of extension types (aka cdef classes) as of the current version (3.1.0a0). There's already an issue on the Cython github repo requesting this feature: https://github.com/cython/cython/issues/3283.
Upvotes: 3