【发布时间】:2016-05-27 14:37:31
【问题描述】:
我有一个用 sympy 编写的 ODE 系统:
from sympy.parsing.sympy_parser import parse_expr
xs = symbols('x1 x2')
ks = symbols('k1 k2')
strs = ['-k1 * x1**2 + k2 * x2', 'k1 * x1**2 - k2 * x2']
syms = [parse_expr(item) for item in strs]
我想将其转换为向量值函数,接受 x 值的 1D numpy 数组,k 值的 1D numpy 数组,返回在这些点评估的方程的 1D numpy 数组。签名看起来像这样:
import numpy as np
x = np.array([3.5, 1.5])
k = np.array([4, 2])
xdot = my_odes(x, k)
我想要这样的东西的原因是把这个函数给scipy.integrate.odeint,所以它需要很快。
尝试 1:潜艇
当然,我可以为subs写一个包装器:
def my_odes(x, k):
all_dict = dict(zip(xs, x))
all_dict.update(dict(zip(ks, k)))
return np.array([sym.subs(all_dict) for sym in syms])
但这超级慢,尤其是对于我的真实系统,它要大得多并且运行了很多次。我需要将此操作编译为 C 代码。
尝试 2:theano
我可以接近sympy's integration with theano:
from sympy.printing.theanocode import theano_function
f = theano_function(xs + ks, syms)
def my_odes(x, k):
return np.array(f(*np.concatenate([x,k]))))
这会编译每个表达式,但是输入和输出的所有这些打包和解包都会减慢它的速度。 theano_function 返回的函数接受 numpy 数组作为参数,但它需要每个符号一个数组,而不是每个符号一个元素。 functify 和 ufunctify 的行为也相同。我不需要广播行为;我需要它将数组的每个元素解释为不同的符号。
尝试 3:DeferredVector
如果我使用DeferredVector,我可以创建一个接受 numpy 数组的函数,但我无法将其编译为 C 代码或返回一个 numpy 数组而不自己打包。
import numpy as np
import sympy as sp
from sympy import DeferredVector
x = sp.DeferredVector('x')
k = sp.DeferredVector('k')
deferred_syms = [s.subs({'x1':x[0], 'x2':x[1], 'k1':k[0], 'k2':k[1]}) for s in syms]
f = [lambdify([x,k], s) for s in deferred_syms]
def my_odes(x, k):
return np.array([f_i(x, k) for f_i in f])
使用DeferredVector 我不需要解包输入,但我仍然需要打包输出。另外,我可以使用lambdify,但ufuncify 和theano_function 版本已失效,因此不会生成快速C 代码。
from sympy.utilities.autowrap import ufuncify
f = [ufuncify([x,k], s) for s in deferred_syms] # error
from sympy.printing.theanocode import theano_function
f = theano_function([x,k], deferred_syms) # error
【问题讨论】: