2015-04-03 3 views
2

Я хочу сделать boolean Numpy массив в Cython с заданным размером другого numpy.array, но это вызывает сообщение об ошибке:Создание булева массива

CosmoTest.pyx

import numpy as np 
cimport numpy as np 
cimport cython 
from libcpp cimport bool 
x=np.array([[-0.3,1.2],[2.5,0.82],[0.61,-0.7]]) 
mask= np.ones_like(x,dtype=bool) 

ошибка:

 mask= np.ones_like(x,dtype=bool) 
            ^
------------------------------------------------------------ 

CosmoTest.pyx:318:39: 'bool' is not a constant, variable or function identifier 

Как это должно быть определено в cython?

Update:

cpdef np.ndarray arc(np.ndarray x): 
    cdef np.ndarray[double, ndim=1, mode='c'] out = np.zeros_like(x) 
    cdef np.ndarray[np.uint8_t,cast=True, ndim=1] mask = (x < 0.999).view(dtype=np.uint8) 
    if mask.any(): 
     out[mask] = 0.5*np.log((1.+((1.-x[mask])/(x[mask]+1.))**0.5)/(1.-((1.-x[mask])/(x[mask]+1.))**0.5))/(1-x[mask]**2)**0.5 

    cdef np.ndarray[np.uint8_t,cast=True, ndim=1] mask = (x > 1.001).view(dtype=np.uint8) 
    if mask.any(): 
     out[mask] = np.arctan(((x[mask]-1.)/(x[mask]+1.))**0.5)/(x[mask]**2 - 1)**0.5 

    cdef np.ndarray[np.uint8_t,cast=True , ndim=1] mask = ((x >= 0.999) & (x <= 1.001)).view(dtype=np.uint8) 
    if mask.any(): 
     out[mask] = 5./6. - x[mask]/3. 

    return out 

Сообщение об ошибке:

Error compiling Cython file: 
------------------------------------------------------------ 
... 
     if mask.any(): 
      out[mask] = 0.5*np.log((1.+((1.-x[mask])/(x[mask]+1.))**0.5)/(1.-((1.-x[mask])/(x[mask]+1.))**0.5))/(1-x[mask]**2)**0.5 

     cdef np.ndarray[np.uint8_t,cast=True, ndim=1] mask = (x > 1.001).view(dtype=np.uint8) 
     if mask.any(): 
      out[mask] = np.arctan(((x[mask]-1.)/(x[mask]+1.))**0.5)/(x[mask]**2 - 1)**0.5 
                ^
------------------------------------------------------------ 

CosmoTest.pyx:9:55: local variable 'mask' referenced before assignment 

ответ

8

При изменении (последняя строка) код для

mask= np.ones_like(x,dtype=np.bool) 

он будет работать (принять bool f rom numpy вместо того, чтобы пытаться использовать определение lipcpp). Однако на самом деле статическая типизация логических массивов numpy не работает в настоящее время (см. Passing a numpy pointer (dtype=np.bool) to C++).

Лучший путь вперед в настоящее время является статически ввести их в качестве

def f(np.ndarray[dtype=np.int8_t,ndim=1] x): 
    cdef np.ndarray[dtype=np.int8_t,ndim=1] y 
    y = np.ones_like(x,dtype=np.int8) 
    return y.view(dtype=np.bool) # returns as boolean array 

Внутренне NumPy использует 8 разрядное целое число, чтобы сохранить логическое значение, и, таким образом, вы можете просто использовать view переосмысливать массив без копирования.

Если у вас булево массив и хотел вызвать f вы могли бы сделать

mask = np.array([True,False,True]) 
f(mask.view(dtype=np.int8)) 

Вы всегда можете написать небольшую функцию-обертку в качестве общедоступного интерфейса f сделать это переосмысление автоматически.

Это более проблематично, чем нужно, но с ним можно работать.

Добавление в ответ на комментарии

В статье я связан предложил использовать cast=True:

cdef np.ndarray[np.uint8_t,cast=True] mask = (x > 0.01) 

Это также работает отлично. Написанная в моем подходе, который не был бы

cdef np.ndarray[np.uint8_t] mask = (x > 0.01).view(dtype=np.uint8) 

(т.е. не литая, но с view). Насколько я могу судить, нет никакой практической разницы, поэтому выберите, какой из них вы считаете более приятным.

и отредактирован, чтобы ответить на дополнительные вопросы,

Рабочий код приведен ниже (я проверил и компилирует - я не проверил, чтобы убедиться, что он работает). Вы получали ошибки компилятора, потому что несколько раз задавали тип mask. Вам разрешено использовать только cdef один раз за каждую переменную за функцию, но определяя тип, который вы можете назначить ему так часто, как вам нравится.

cpdef np.ndarray arc(np.ndarray x): 
    cdef np.ndarray[double, ndim=1, mode='c'] out = np.zeros_like(x) 
    cdef np.ndarray[np.uint8_t, ndim=1] mask = (x < 0.999).view(dtype=np.uint8) 
    if mask.any(): 
     out[mask] = 0.5*np.log((1.+((1.-x[mask])/(x[mask]+1.))**0.5)/(1.-((1.-x[mask])/(x[mask]+1.))**0.5))/(1-x[mask]**2)**0.5 

    mask = (x > 1.001).view(dtype=np.uint8) # REMOVED cdef! 
    if mask.any(): 
     out[mask] = np.arctan(((x[mask]-1.)/(x[mask]+1.))**0.5)/(x[mask]**2 - 1)**0.5 

    mask = ((x >= 0.999) & (x <= 1.001)).view(dtype=np.uint8) # REMOVED cdef! 
    if mask.any(): 
     out[mask] = 5./6. - x[mask]/3. 

    return out 

(Я также удалил cast=True из определения. Это не важно. Вы можете использовать это, или использовать view(dtype=np.uint8). Вы можете использовать оба, если вам нравится, но это больше писать!)

+0

В принципе, я хочу сделать эту операцию, и мне интересно, как это должно быть сделано с вашим подходом 'cdef np.ndarray [np.uint8, cast = True] mask = (x> 0.01)'? – Dalek

+0

@Dalek Единственное, что вы ошиблись в строке, которую вы просто указали, - это добавить '_t' после' np.uint8' (т. Е. 'Np.ndarray [np.uint8_t, cast = True] ...') , Необходимость добавления '_t' при указании типов numpy в операторах' cdef' - это просто странность cython. – DavidW

+0

Для справки, в Cython вы можете использовать маску, как для логической индексации (т. Е. 'X [mask] = 10' отлично работает). Если вы передадите маску обратно в Python, сделайте это как 'return mask.view (dtype = np.bool)' – DavidW

Смежные вопросы