Cython: недопустимое использование слитых типов, тип не может быть специализированным - PullRequest
0 голосов
/ 28 июня 2018

У меня есть следующий MCVE:

import numpy as np

cimport numpy as np
cimport cython

from cython cimport floating


def func1(floating[:] X_data, floating alpha):
    if floating is double:
        dtype = np.float64
    else:
        dtype = np.float32

    cdef floating[:] prios = np.empty(12, dtype=dtype)
    cdef int ws_size = 10

    C = np.argpartition(np.asarray(prios), ws_size)[:ws_size].astype(np.int32)

    cdef int res = func2(X_data, alpha, C)


cpdef int func2(floating[:] X_data, floating alpha, int[:] C):
    cdef int epoch = 1
    return epoch

Попытка запустить cython test_fused.pyx дает мне:

Error compiling Cython file:
------------------------------------------------------------
...
    cdef floating[:] prios = np.empty(12, dtype=dtype)
    cdef int ws_size = 10

    C = np.argpartition(np.asarray(prios), ws_size)[:ws_size].astype(np.int32)

    cdef int res = func2(X_data, alpha, C)
                       ^
------------------------------------------------------------

test_fused.pyx:21:24: no suitable method found

    Error compiling Cython file:
    ------------------------------------------------------------
    ...
        cdef floating[:] prios = np.empty(12, dtype=dtype)
        cdef int ws_size = 10

        C = np.argpartition(np.asarray(prios), ws_size)[:ws_size].astype(np.int32)

        cdef int res = func2(X_data, alpha, C)
                           ^
    ------------------------------------------------------------

    test_fused.pyx:21:24: no suitable method found

    Error compiling Cython file:
    ------------------------------------------------------------
    ...
        cdef floating[:] prios = np.empty(12, dtype=dtype)
        cdef int ws_size = 10

        C = np.argpartition(np.asarray(prios), ws_size)[:ws_size].astype(np.int32)

        cdef int res = func2(X_data, alpha, C)
                      ^
    ------------------------------------------------------------

    test_fused.pyx:21:19: Invalid use of fused types, type cannot be specialized

    Error compiling Cython file:
    ------------------------------------------------------------
    ...
        cdef floating[:] prios = np.empty(12, dtype=dtype)
        cdef int ws_size = 10

        C = np.argpartition(np.asarray(prios), ws_size)[:ws_size].astype(np.int32)

        cdef int res = func2(X_data, alpha, C)
                      ^
    ------------------------------------------------------------

    test_fused.pyx:21:19: Invalid use of fused types, type cannot be specialized

У меня был более сложный код, который также передавал массив C как определенное во время выполнения значение, которое не вызывало никаких проблем. В чем причина этой ошибки компиляции?

Я озадачен, потому что небольшие модификации (добавление фиктивного ключевого слова arg к func1 и двух ключевых аргументов к func2) делают код компиляцией:

def func1(floating[:] X_data, floating alpha,
          int dummy_variable=1):  # added dummy_variable here

    # same as before here

    cdef int res = func2(X_data, alpha, C,
                       dummy_variable=dummy_variable)


cpdef int func2(floating[:] X_data, floating alpha, int[:] C, 
    int K=6, int dummy_variable=1):  # added K and dummy variable here

    cdef int epoch = 1
    return epoch

1 Ответ

0 голосов
/ 28 июня 2018

Давайте начнем с меньшего репродуктора:

%%cython
import numpy as np
from cython cimport floating

def func1(floating[:] X_data):
    C = np.empty(12, dtype=np.int_32)
    func2(X_data, C)

cpdef func2(floating[:] X_data, int[:] C):
    pass

Не компилируется.

Одно важное замечание: func2 - это cpdef, это означает, что Cython будет вызывать его как raw-C-функцию из func1. Эти две С-сигнатуры будут созданы Cython для слитой функции func2 (одна для double и одна для float):

static PyObject *__pyx_fuse_0__pyx_f_4test_func2(__Pyx_memviewslice, __Pyx_memviewslice, int __pyx_skip_dispatch); /*proto*/
static PyObject *__pyx_fuse_1__pyx_f_4test_func2(__Pyx_memviewslice, __Pyx_memviewslice, int __pyx_skip_dispatch); /*proto*/

Таким образом, ожидается, что C будет __Pyx_memviewslice, но для Cython это PyObject в func1, поэтому функция не может быть вызвана как cdef. Что я не понимаю: почему Cython не возвращается к python- def версии?

Сигнатура C немного вводит в заблуждение, и Cython делает больше проверок типов во время компиляции, поэтому это не поможет определить C как

cdef float[:] C

потому что даже если C в этом случае также __Pyx_memviewslice, он не имеет правильного типа, и только

cdef int[:] C

будет работать.

Если func2 было определено как

cpdef func2(floating[:] X_data, C):

соответствующие две С-подписи будут

static PyObject *__pyx_fuse_0__pyx_f_4test_func2(__Pyx_memviewslice, PyObject *, int __pyx_skip_dispatch); /*proto*/
static PyObject *__pyx_fuse_1__pyx_f_4test_func2(__Pyx_memviewslice, PyObject *, int __pyx_skip_dispatch); /*proto*/

, поэтому можно передать C, что является PyObject для этой функции.

Итак, есть два способа решения проблемы компиляции:

  • используйте cdef int[:] C в func1 или
  • падение int[:] C в подписи func2

Так почему же добавляется фиктивный параметр, т.е.

%%cython -a
import numpy as np
from cython cimport floating

def func1(floating[:] X_data, int dummy_variable=1):
    C = np.empty(12, dtype=np.int_32)
    func2(X_data, C, dummy_variable=dummy_variable)

cpdef func2(floating[:] X_data, int[:] C, int k=6, dummy_variable = 1):
    pass

работает

На самом деле существует третий способ компиляции кода: сделать func2 функцию только для Python def. В этом случае тип C не играет роли во время компиляции и будет проверен во время выполнения.

Дело в том, что в случае с фиктивными переменными Cython решает вызвать func2 как Python-функцию, а не как C-функцию, и, таким образом, несоответствие типов не играет роли.

Это легко увидеть, просмотрев аннотированный html-файл.

Однако я не могу сказать, что является причиной того, что Cython прибегает к вызову Python-функции для вашего обходного пути. Все, что я могу сказать: не предоставление значения для k играет важную роль.

...