Почему Cython ожидает 0 измерений?

Я свел свою проблему к небольшому воспроизводимому тестовому примеру:

В файле 1 (custom_cython.pyx) у меня есть следующее:

import numpy as np
cimport numpy as np
cimport cython

ctypedef np.uint8_t DTYPE_B_t
ctypedef np.uint16_t CELL_ID_t
ctypedef np.int64_t DTYPE_INT64_t


cdef struct LOOKUPMEM_t:
    DTYPE_B_t filled_flag
    CELL_ID_t key_i
    CELL_ID_t key_j
    CELL_ID_t key_k
    DTYPE_INT64_t offset
    DTYPE_INT64_t num_elements  


cdef LOOKUPMEM_t[:] lookup_memory


my_dtype = [("filled_flag", np.uint8, 1),
            ("ijk", np.uint16, 3),
            ("offset_and_num", np.int64, 2)]

input_numpy_dtype = np.dtype(my_dtype, align=True)

lookup_memory = np.zeros(5000, dtype=input_numpy_dtype)

В файле 2 (custom_cython_test.py) у меня есть следующее:

from custom_cython import lookup_memory

print(lookup_memory)

Когда я запускаю python custom_cython_test.py, я получаю ValueError: Expected 0 dimension(s), got 1 в строке lookup_memory = np.zeros(5000, dtype=input_numpy_dtype)

В моем определении структуры я пытался использовать cdef packed struct LOOKUPMEM_t и align=False при создании dtype, и это дает ту же ошибку.

Я использую Python 3.7.3 с Cython версии 0.29.12 и Numpy 1.16.4.

Раньше я успешно назначал cython memoryviews массивам 1-D numpy, поэтому я сбит с толку тем, почему мой очевидный 1d cdef LOOKUPMEM_t[:] lookup_memory ожидает 0 измерений. Кто-нибудь может сказать мне, что происходит?


person Steve O'Neill    schedule 18.02.2020    source источник
comment
Что бы это ни стоило, я смог воспроизвести с помощью Python 3.6.8 и Cython 0.29.15. Пока не знаю, в чем проблема.   -  person Iguananaut    schedule 18.02.2020
comment
Спасибо, по крайней мере, я знаю, что я не сумасшедший!   -  person Steve O'Neill    schedule 18.02.2020
comment
Рассматриваемая ошибка, по-видимому, возникла в коде, сгенерированном Cython, из довольно сложной функции с именем __Pyx_ValidateAndInit_memviewslice. Я не совсем знаком с областями Typed Memoryview в Cython, поэтому до сих пор не ясно, что это значит, не прочитав этот код более внимательно...   -  person Iguananaut    schedule 18.02.2020
comment
И что интересно, если вы измените его на lookup_memory = np.zeros(5000, dtype=input_numpy_dtype).reshape(1,5000), он станет ValueError: Buffer has wrong number of dimensions (expected 1, got 2), что немного отличается от форматирования предыдущей ошибки, странно?   -  person Steve O'Neill    schedule 18.02.2020
comment
Не странно; последняя ошибка связана с тем, что на самом деле установка формы на (1, 5000) делает ее двумерным массивом, поэтому вам нужно объявить cdef LOOKUPMEM_t[:, :] lookup_memory, чтобы он был двумерным. Если вы это сделаете, первая ошибка все равно произойдет.   -  person Iguananaut    schedule 18.02.2020
comment
Извините, я имел в виду, что странно было то, что форматирование ошибки было другим, при использовании .reshape(1,5000) формат ошибки совпадает. Буфер имеет неправильное количество измерений (ожидалось %d, получено %d) из github.com/cython/cython/blob/master/Cython/Utility/, но без .reshape формат Ожидается 0 измерений, получено 1, которое, возможно, не из функции __Pyx_ValidateAndInit_memviewslice?   -  person Steve O'Neill    schedule 18.02.2020


Ответы (1)


Проблема, похоже, заключается в этой части вашей структуры:

    CELL_ID_t key_i
    CELL_ID_t key_j
    CELL_ID_t key_k

в сочетании с этой частью вашего dtype:

("ijk", np.uint16, 3)

и аналогично для вашего комбинированного поля offset_and_num.

Проблема в том, что когда интерфейс memoryview видит похожее на кортеж поле, такое как ("ijk", np.uint16, 3), он хочет распаковать его в одномерный массив из 3 элементов, но следующим ключом в структуре является просто CELL_ID_t key_i, скаляр 0-D.

Если я изменю вашу структуру, чтобы она более точно соответствовала dtype Numpy, она работает:

cdef struct LOOKUPMEM_t:
    DTYPE_B_t filled_flag
    CELL_ID_t ijk[3]
    DTYPE_INT64_t offset_num_elements[2]

Итак, у вас есть несколько вариантов дальнейших действий. Если вы действительно хотите сохранить свою структуру такой же, вы можете сделать это и отформатировать dtype по-другому. Поскольку легко просматривать массивы Numpy с разными типами dtype, вы также можете использовать другой тип dtype для инициализации представления памяти, если хотите сохранить существующий формат dtype для других случаев использования.

person Iguananaut    schedule 18.02.2020