Почему компиляция Numba pycc прерывается? - PullRequest
0 голосов
/ 15 ноября 2018

Сначала немного контекста: я пытаюсь интегрировать связанный ODE, используя scipy.integrate.odeint очень часто с различными начальными условиями x_ и параметрами r_ и d_. Я пытаюсь ускорить интеграцию, заблаговременно компилируя правую часть ODE и пытаясь сократить время вызова функции odeint.

Я пытаюсь скомпилировать функцию python, используя numba.pycc.CC заблаговременно. Это работает для простых функций, таких как:

import numpy
from numba.pycc import CC

cc = CC('x_test')
cc.verbose = True

@cc.export('x_test', 'f8(f8[:])')
def x_test(y):
    return numpy.sum(numpy.log(y) * .5) # just a random combination of numpy functions I used to test the compilation

cc.compile() 

Фактическая функция, которую я пытаюсь скомпилировать, выглядит следующим образом:

# code_generation3.py
import numpy
from numba.pycc import CC

"""
N = 94
input for x_dot_all could look like:
    x_ = numpy.ones(N * 5)
    x[4::5] = 5e13
    t_ := some float from a numpy linspace. it is passed by odeint.
    r_ = numpy.random.random(N * 4)
    d_ = numpy.random.random(N * 4) * .8

    In practice the size of x_ is 470 and of r_ and d_ is 376.
"""

cc = CC('x_temp_dot1')
cc.verbose = True

@cc.export('x_temp_dot1', 'f8[:](f8[:], f8, f8[:], f8[:], f8[:])')
def x_dot_all(x_,t_,r_,d_, h):
    """
    rhs of the lotka volterra equation for all "patients"
    :param x: initial conditions, always in groupings of 5: the first 4 is the bacteria count, the 5th entry is the carrying capacity
    :param t: placeholder required by odeint
    :param r: growth rates of the types of bacteria
    :param d: death rates of the types of bacteria

    returns the right hand side of the competitive lotka-volterra equation with finite and shared carrying capacity in the same ordering as the initial conditions 
    """
        def x_dot(x, t, r, d, j):
        """
        rhs of the differential equation describing the intrahost evolution of the bacteria
        :param x: initial conditions i.e. bacteria sizes and environmental carrying capacity
        :param t: placeholder required by odeint
        :param r: growth rates of the types of bacteria
        :param d: death rates of the bacteria
        :param j: placeholder for the return value

        returns the right hand side of the competitive lotka-volterra equation with finite and shared carrying capacity
        """

        j[:-1] = x[:-1] * r * (1 - numpy.sum(x[:-1]) / x[-1]) - d * x[:-1]
        j[-1]   = -numpy.sum(x[:-1])
        return j 

    N = r_.shape[0]
    j = numpy.zeros(5)
    g = [x_dot(x_[5 * i : 5 * (i+1)], t_, r_[4 * i : 4* (i+1)], d_[4 * i: 4 * (i+1)], j) for i in numpy.arange(int(N / 4) )]

    for index, value in enumerate(g):
        h[5 * index : 5 * (index + 1)] = value

    return h

cc.compile()

Здесь я получаю следующее сообщение об ошибке:

[xxxxxx@xxxxxx ~]$ python code_generation3.py 
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++ [enabled by default]
generating LLVM code for 'x_temp_dot1' into /tmp/pycc-build-x_temp_dot1-wyamkfsy/x_temp_dot1.cpython-36m-x86_64-linux-gnu.o
python: /root/miniconda3/conda-bld/llvmdev_1531160641630/work/include/llvm/IR/GlobalValue.h:233: void llvm::GlobalValue::setVisibility(llvm::GlobalValue::VisibilityTypes): Assertion `(!hasLocalLinkage() || V == DefaultVisibility) && "local linkage requires default visibility"' failed.
Aborted

Мне интересно, что я сделал не так?

Обе функции работают с @jit(nopython = True) декоратором. К своему стыду, я также пытался жестко запрограммировать понимание списка (чтобы избежать циклов for и дальнейших вызовов функций), но с этой же проблемой.

Я знаю, что способ обработки / создания возвращаемых значений h и j, соответственно, не является ни эффективным, ни элегантным, но у меня были проблемы с получением возвращаемого значения в правильной форме для odeint, потому что numba плохо справляется с numpy.reshape.

Я искал справку по numba , но это не помогло мне понять мою проблему. Я искал сообщение об ошибке, но нашел только ссылку , которая может быть похожа. Однако понижение numba до 0.38.0 у меня не сработало.

Спасибо всем!

1 Ответ

0 голосов
/ 16 ноября 2018

Полагаю, это сработает, если сначала скомпилировать x_dot, а потом x_dot_all. В любом случае, я бы порекомендовал объединить эти две функции.

Циклы в общем случае не являются проблемой в пределах Numba, но, конечно, есть и списки. Также старайтесь избегать чрезмерного количества маленьких петель. (Векторизованные команды, например. numpy.sum(x[:-1]) - это все отдельные циклы). Иногда Numba может комбинировать эти циклы для получения эффективного кода, но не каждый раз.

Пример

# code_generation3.py
import numpy
import numba as nb
from numba.pycc import CC

cc = CC('x_dot_all')
cc.verbose = True


@cc.export('x_dot_all_mod', 'f8[:](f8[:], f8, f8[:], f8[:], f8[:])')
def x_dot_all(x_,t_,r_,d_, h):
  N = r_.shape[0]

  for i in range(int(N / 4)):
    sum_x=x_[5*i+0]+x_[5*i+1]+x_[5*i+2]+x_[5*i+3]
    TMP=1.-(sum_x)/x_[5*i+4]

    h[i*5+0]=x_[i*5+0]*r_[4*i+0]*TMP-d_[4*i+0]*x_[i*5+0]
    h[i*5+1]=x_[i*5+1]*r_[4*i+1]*TMP-d_[4*i+1]*x_[i*5+1]
    h[i*5+2]=x_[i*5+2]*r_[4*i+2]*TMP-d_[4*i+2]*x_[i*5+2]
    h[i*5+3]=x_[i*5+3]*r_[4*i+3]*TMP-d_[4*i+3]*x_[i*5+3]
    h[i*5+4]=-sum_x

  return h

if __name__ == "__main__":
    cc.compile()

Производительность

N=94
x_ = np.ones(N * 5)
x_[4::5] = 5e13
t_ = 15
r_ = np.random.random(N * 4)
d_ = np.random.random(N * 4) * .8
h = np.zeros(N * 5)

#your version: 38 µs
#new version:  1.8µs
...