I am writing cython code with cython v3.1.5.
I have defined these fused types:
ctypedef fused index_t:
int32_t
int64_t
ctypedef fused value_t:
double
double complex
and am trying to use this code to initialize my UMFFactor object with a scipy.sparse.csc_array object:
cdef class UMFFactor:
cdef void* _symbolic # member to be initialized with C call in __cinit__
# ... etc.
def __cinit__(self, object A):
A, use_int32, _ = validate_csc_input(A)
self._use_int32 = use_int32
self._is_real = _is_real_dtype(A.dtype)
# Compute the symbolic analysis with C function call
cdef index_t[::1] indptr = A.indptr
cdef index_t[::1] indices = A.indices
cdef value_t[::1] data = A.data
if self._is_real:
if self._use_int32:
status = umfpack_di_symbolic(
M,
N,
&indptr[0],
&indices[0],
&data[0],
&self._symbolic,
self._control.data,
self._info.data
)
else:
# ... etc. to dispatch the other C function calls.
but I get this compiler error:
Error compiling Cython file:
------------------------------------------------------------
...
# Compute the symbolic analysis
cdef Py_ssize_t M = A.shape[0]
cdef Py_ssize_t N = A.shape[1]
cdef index_t[::1] indptr = A.indptr
^
------------------------------------------------------------
sksparse/umfpack.pyx:679:36: Cannot coerce to a type that is not specialized
I have also tried defining a C function like:
cdef void _init_symbolic(self, index_t[::1] indptr, index_t[::1] indices, value_t[::1] data)
that __cinit__ calls, but I get a similar error:
sksparse/umfpack.pyx:681:27: no suitable method found (candidates: 4)
The only working method I have found is to manually define 4 functions for each type memoryview input that I need, and skip the fused types altogether.
Is there something I am missing about how to use fused types? Or can they not be used on arbitrary object members, even though I know those members will be C-contiguous numpy arrays with my desired fused types?
Fused types don't exist in complete isolation - they're intended to be used in a "fused function" where one or more arguments are fused types. The idea is that you call a specialized version of the function where every instance of index_t is replaced by either int32_t or int64_t. Therefore your proposed usage doesn't really work - it needs to know the type at the point the function is called.
(In addition, "special" functions of cdef classes, like __cinit__, __add__, etc, also don't work well with fused types: they have a different dispatch mechanism to most other Cython functions so I think currently fused types aren't handled there).
Your approach of defining a function like
cdef void _init_symbolic(self, index_t[::1] indptr, index_t[::1] indices, value_t[::1] data)
is basically the right one. It's hard to be sure without seeing the exact code. However, cdef fused functions are dispatched at compile time (and thus Cython needs to be able to work out which one to call from the arguments that you're passing it it). If you make it a def function then it is dispatched at runtime by working out what the Python object arguments you pass are.
I suspect this is the change you need to make.