3

I try to rewrite a complex function form Python to Cython to speed it up massively and I encounter the following problem: while compiling my function hh_vers_vector.pyx using

setup(
    ext_modules=cythonize("hh_vers_vector.pyx"),
)

it throws the following error

    cdef int numSamples = len(Iext);

    # initial values
    cdef float v[numSamples]
                      ^
    ------------------------------------------------------------

    hh_vers_vector.pyx:47:27: Not allowed in a constant expression

If, however, I give "numSamples" as a number into the function, there is no problem. I don't understand that, because I thought that len(Iext) will return a number as 10.000 as well. So why does Cython have a problem with this expression?

cdef float v[numSamples] # ERROR
cdef float v[10000]      # NO ERROR

My function looks like this so far:

from math import exp
import time

def hhModel(*params, Iext, float dt, int Vref):

    ## Unwrap params argument: these variables are going to be optimized
    cdef float ENa = params[0]
    cdef float EK  = params[1]
    cdef float EL  = params[2]
    cdef float GNa = params[3]
    cdef float GK  = params[4]
    cdef float GL  = params[5]

    ## Input paramters
    # I  : a list containing external current steps, your stimulus vector [nA/1000]
    # dt : a crazy time parameter [ms]
    # Vref : reference potential [mV]
    # T  : Total simulation time in [ms]

    def alphaM(float v, float vr):       return 0.1 * (v-vr-25) / ( 1 - exp(-(v-vr-25)/10) )
    def betaM(float v, float vr):        return 4 * exp(-(v-vr)/18)
    def alphaH(float v, float vr):       return 0.07 * exp(-(v-vr)/20)
    def betaH(float v, float vr):        return 1 / ( 1 + exp( -(v-vr-30)/10 ) )
    def alphaN(float v, float vr):       return 0.01 * (v-vr-10) / ( 1 - exp(-(v-vr-10)/10) )
    def betaN(float v, float vr):        return 0.125 * exp(-(v-vr)/80)

    ## steady-state values and time constants of m,h,n

    def m_infty(float v, float vr):      return alphaM(v,vr) / ( alphaM(v,vr) + betaM(v,vr) )
    def h_infty(float v, float vr):      return alphaH(v,vr) / ( alphaH(v,vr) + betaH(v,vr) )
    def n_infty(float v, float vr):      return alphaN(v,vr) / ( alphaN(v,vr) + betaN(v,vr) )

    ## parameters
    cdef float Cm, gK, gL, INa, IK, IL, dv_dt, dm_dt, dh_dt, dn_dt, aM, bM, aH, bH, aN, bN
    cdef float Smemb = 4000    # [um^2] surface area of the membrane
    cdef float Cmemb = 1       # [uF/cm^2] membrane capacitance density
    Cm = Cmemb * Smemb * 1e-8  # [uF] membrane capacitance

    gNa = GNa * Smemb * 1e-8   # Na conductance [mS]
    gK  = GK  * Smemb * 1e-8   # K conductance [mS]
    gL  = GL  * Smemb * 1e-8   # leak conductance [mS]

    ######### HERE IS THE PROBLEM ##############
    cdef int numSamples = len(Iext);
    ######### HERE IS THE PROBLEM ##############

    # initial values
    cdef float v[numSamples]
    cdef float m[numSamples]
    cdef float h[numSamples]
    cdef float n[numSamples]

    v[0]  = Vref                    # initial membrane potential
    m[0]  = m_infty(v[0], Vref)     # initial m
    h[0]  = h_infty(v[0], Vref)     # initial h
    n[0]  = n_infty(v[0], Vref)     # initial n

    ## calculate membrane response step-by-step
    for j in range(0, numSamples-1):

        # ionic currents: g[mS] * V[mV] = I[uA]
        INa = gNa * m[j]*m[j]*m[j] * h[j] * (ENa-v[j])
        IK = gK * n[j]*n[j]*n[j]*n[j] * (EK-v[j])
        IL = gL * (EL-v[j])

        # derivatives
        # I[uA] / C[uF] * dt[ms] = dv[mV]
        dv_dt = ( INa + IK + IL + Iext[j]*1e-3) / Cm;

        aM = 0.1 * (v[j]-Vref-25) / ( 1 - exp(-(v[j]-Vref-25)/10))
        bM = 4 * exp(-(v[j]-Vref)/18)
        aH = 0.07 * exp(-(v[j]-Vref)/20)
        bH = 1 / ( 1 + exp( -(v[j]-Vref-30)/10 ) )
        aN = 0.01 * (v[j]-Vref-10) / ( 1 - exp(-(v[j]-Vref-10)/10) )
        bN = 0.125 * exp(-(v[j]-Vref)/80)

        dm_dt = (1-m[j])* aM - m[j]*bM
        dh_dt = (1-h[j])* aH - h[j]*bH
        dn_dt = (1-n[j])* aN - n[j]*bN

        # calculate next step
        v[j+1] = (v[j] + dv_dt * dt)
        m[j+1] = (m[j] + dm_dt * dt)
        h[j+1] = (h[j] + dh_dt * dt)
        n[j+1] = (n[j] + dn_dt * dt)

    return v
Svenno Nito
  • 635
  • 1
  • 6
  • 22
  • Possible duplicate of [Cython "Not allowed in a constant expression", boundscheck False doesn't work](https://stackoverflow.com/questions/46306737/cython-not-allowed-in-a-constant-expression-boundscheck-false-doesnt-work) – DavidW Oct 20 '17 at 14:47
  • I saw that post, but it didn't really help.. – Svenno Nito Oct 20 '17 at 14:55
  • I think @danny's answer to this one is a bit better explained, but it does say largely the same thing (so I'm not sure why that answer helped and the other post didn't...). The suggestion to use memoryviews given in the other question is the most sensible solution (much easier than malloc/free) – DavidW Oct 20 '17 at 17:22
  • Cool, I will consider this for my final solution! @danny's answer explained why I got the error I got. The other post may relate to the same problem, but as someone completely new to cython and python I am not able to relate them. So I'm grateful I got an answer despite similiarity. – Svenno Nito Oct 21 '17 at 04:45

1 Answers1

7

cdef in Cython is for C definitions, as the name implies. Therefore the code

cdef float v[10000]

is translated to the following C code

float v[10000];

Meaning a static float array of size 10k, defined at compile time.

This, on the other hand

cdef float v[numSamples]

Would translate to the C code

int numSamples = <..>;
float v[numSamples];

This is an attempt to compile a static array containing a variable number of elements, which is not valid C. Hence the 'constant expression error'.

Either use a python list to store the floats, or dynamically allocate C arrays via malloc/free.

danny
  • 5,140
  • 1
  • 19
  • 31
  • Thanks for your comment! I guess pythonl lists would be slower than C arrays right? I've never used malloc nor free before, do you know how the code would need to look like? – Svenno Nito Oct 20 '17 at 14:27
  • 1
    btw, using `DEF numSamples = 200000` also works, but then I don't understand your explanation anymore. – Svenno Nito Oct 20 '17 at 14:59
  • 1
    Static arrays cannot be allocated at compile time with a variable number. They are static. A statically defined number like the above is static/constant. An int variable that changes at run time is not. – danny Oct 20 '17 at 15:10
  • 3
    `DEF` is a compile time constant. – DavidW Oct 20 '17 at 15:59
  • I think now i understood the concept of staticness(?)! Thanks a bunch. – Svenno Nito Oct 21 '17 at 04:47