加快odeint计算

时间:2017-03-30 04:12:12

标签: python scipy ode odeint

我使用scipy的odeint运行一个相当大的ODE系统。为了使系统保持连贯,易于阅读和更短,我将大部分内容划分为相互调用的类。从可读性的角度来看,似乎工作正常,但是由于重复调用python对象,它会显着减慢实际的ODE求解器。我研究了优化代码的方法,以保持一定程度的可读性,同时使计算更有效率(使用Numba或使用Sympy将其转换为numpy-aware lambda函数),但是到目前为止我还没有成功。我想知道在这种情况下哪些策略可能会有所帮助。我在下面提供了一个简单的代码版本。先感谢您。

class NaL():
    g = 0
    def I(self, v, E): return self.g * (v - E) #Leak current

class Neuron():
    C_m = 1.0 #membrane capacitance
    V = 0  # Voltage
    m, h = 0, 0  #activating variables
    def I_Na(self): #Sodium Currents
        return self.NaT.I(self.V, self.m, self.h)+ self.NaL.I(self.V)  
    def __init__(self):
        self.NaT = NaT()
        self.NaL = NaL()
        self.NaT.g = 3000
        self.NaL.g = 20

I1 = Neuron()

def Run(X,t):
    I1.V, I1.m, I1.h = X 
    dydt = [(0 - I1.I_Na()) / I1.C_m, #dV/dt for neuron
            I1.NaT.dmdt(I1.V, I1.m), #dm/dt for sodium channel
            I1.NaT.dhdt(I1.V, I1.h) #dh/dt for sodium channel
           ]
    return dydt
X = odeint(Run, [-70, 0.0050, 0.9961], t)

1 个答案:

答案 0 :(得分:0)

集成的瓶颈几乎肯定会对衍生from jitcode import jitcode, provide_basic_symbols t, y = provide_basic_symbols() class NaL(): […] class Neuron(): […] I1 = Neuron() I1.V, I1.m, I1.h = y(0), y(1), y(2) Run = [ (0 - I1.I_Na()) / I1.C_m, I1.NaT.dmdt(I1.V, I1.m), I1.NaT.dhdt(I1.V, I1.h) ] t = range(100) ODE = jitcode(Run) ODE.set_initial_value([-70,0.0050,0.9961], t[0]) X = np.vstack(ODE.integrate for time in t[1:]) 进行评估,因为每次集成都必须多次调用它。现在,由于所有类成员和函数调用,你在那里有相当大的Python开销。

对此的一个解决方案是真正使用模块JiTCODE,而不是定义积分器使用的函数,而是用符号(使用SymPy)定义微分方程。然后将它们转换为C代码,将其编译为Python函数,然后将其用于集成。这样,您的类和类函数在实际集成之前仅被调用几次,并且集成与您在没有类和类似的情况下定义衍生函数时一样高效。 (另外,通过编译可以获得相当大的速度提升,特别是如果你有很多神经元的话。)

目前,您将示例翻译为JiTCODE非常简单:

math.sin

此代码现在无法正常工作,原因与您的示例代码不起作用的原因相同。请注意,类中涉及动态变量的所有算术都是符号化的。使用添加和乘法,这是开箱即用的,但如果您有sympy.sinsympy.sin之类的内容,则必须将其替换为{{1}}。