Dalek
Dalek

Reputation: 4318

Making a boolean array

I want to make a boolean numpy array in cython with the given size of another numpy.array but it raises an error message:

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)

error:

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

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

How should it be defined in 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 Message:

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

Upvotes: 2

Views: 2808

Answers (1)

DavidW
DavidW

Reputation: 30941

If you change (the last line of) your code to

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

it will work (take bool from numpy rather than trying to use the lipcpp definition). However, actually statically typing boolean numpy arrays doesn't quite work currently (see Passing a numpy pointer (dtype=np.bool) to C++).

The best way forward currently is to statically type them as

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

Internally numpy uses an 8 bit integer to store a bool, and thus you can just use view to reinterpret the array without copying.

If you had a boolean array and wanted to call f you'd do

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

You could always write a small wrapper function as your public interface to f to do that reinterpretation automatically.

It's more fiddly than it needs be be, but it is possible to work with.

Addition in response to comments

The article I linked to suggested using cast=True:

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

This also works fine. Written in my approach that would be

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

(i.e. no cast, but with a view). As far as I can tell there's no practical difference, so pick which one you think looks nicer.

And edited to respond to additional issues

The working code is below (I've checked and it compiles - I haven't checked to make sure it runs). You were getting compiler errors because you'd defined the type of mask multiple times. You're only allowed to use cdef once per variable per function, but having defined the type you can assign to it as often as you like.

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

(I've also removed cast=True from the definition. This isn't important. You can either use that, or use view(dtype=np.uint8). You can use both if you like, but it's more typing!)

Upvotes: 8

Related Questions