SymPy不能lambdify产品

时间:2016-06-15 21:57:11

标签: python sympy

我正在使用SymPy 1.0和Python 2.7。我想计算前100个整数的总和:

此代码成功运行

import sympy as sy
from sympy.tensor import IndexedBase, Idx
import numpy as np

x = sy.IndexedBase('x')
i = sy.symbols('i', cls=Idx)
s = sy.Sum(x[i], (i, 0, 100))
s_lambda = sy.lambdify(sy.DeferredVector('x'), s, 'numpy')
s_lambda(np.arange(101))

按预期给出5050。但是,当我尝试使用Product代替Sum

import sympy as sy
from sympy.tensor import IndexedBase, Idx
import numpy as np

x = sy.IndexedBase('x')
i = sy.symbols('i', cls=Idx)
s = sy.Product(x[i], (i, 0, 100))
s_lambda = sy.lambdify(sy.DeferredVector('x'), s, 'numpy')
s_lambda(np.arange(101))

我得到了NameError: global name 'Product' is not defined 我究竟做错了什么?是否有解决方法来获得我想要的东西?

编辑1: 如果我事先不知道Product的限制怎么办?让我们来说像

import sympy as sy
from sympy.tensor import IndexedBase, Idx
import numpy as np

x = sy.IndexedBase('x')
i = sy.symbols('i', cls=Idx)
n = sy.symbols('n', integer=True, positive=True)
s = sy.Product(x[i], (i, 0, n))
s_lambda = sy.lambdify((sy.DeferredVector('x'), n) s.doit(), 'numpy')
s_lambda(np.arange(101), 5)

编辑2: 我试图找到一种解决方法。由于这个原因,我发现了NameError: global name 'Product' is not defined错误:

lambdastr((sy.DeferredVector('x'), n), p)

这给出了:

lambda x,n: (Product(x[i], (i, 0, n)))

虽然Sum我们得到了一个有效的lambda函数:

lambda x,n: ((builtins.sum(x[i] for i in range(0, n+1))))

此时问题围绕Product函数的定义。根据手册,我可以通过dict我的函数定义来注入

def my_prod(a, b):
    # my implementation
    pass

my_fun = {"Product" : my_prod}
f = sy.lambdify((sy.DeferredVector('x'), n), p, modules=['numpy', my_fun])
f([1,2,3,4,5], 2)

问题是,当我尝试使用lambdified函数list indices must be integers, not Symbol时,会出现f错误。我想这是因为i是一个符号,而它应该是一个整数。我无法理解为什么在尝试致电integer之前未通过实际的my_prod,而是Sum案例。

1 个答案:

答案 0 :(得分:4)

预先知道Product的限制

您可以通过调用.doit()Product扩展为其组成部分来解决此问题:

In [104]: s = sy.Product(x[i], (i, 1, 10)); s
Out[104]: Product(x[i], (i, 1, 10))

In [105]: s.doit()
Out[105]: x[1]*x[2]*x[3]*x[4]*x[5]*x[6]*x[7]*x[8]*x[9]*x[10]

例如,

import sympy as sy
from sympy.tensor import IndexedBase, Idx
import numpy as np

x = sy.IndexedBase('x')
i = sy.symbols('i', cls=Idx)
s = sy.Product(x[i], (i, 1, 10))
s_lambda = sy.lambdify(sy.DeferredVector('x'), s.doit(), 'numpy')
print(s_lambda(np.arange(11)))

打印

3628800

但是,如果您将.doit()sy.Product(x[i], (i, 1, 100))一起使用,那么您将获得算术溢出,因为np.arange(101)具有dtype int32int64(取决于您的OS)和产品100!

In [109]: math.factorial(100)
Out[109]: 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

太大,无法存储在int32int64数组值中。

In [118]: np.iinfo('int64').max
Out[118]: 9223372036854775807

In [119]: np.iinfo('int64').max < math.factorial(100)
Out[119]: True

因此,

s = sy.Product(x[i], (i, 1, 100))
s_lambda = sy.lambdify(sy.DeferredVector('x'), s.doit(), 'numpy')
print(s_lambda(np.arange(101)))

提出

RuntimeWarning: overflow encountered in long_scalars

并错误地打印0

如果您将dtype int64数组的输入更改为Python int的列表,那么 产品可以正确计算:

import sympy as sy
from sympy.tensor import IndexedBase, Idx
import numpy as np

x = sy.IndexedBase('x')
i = sy.symbols('i', cls=Idx)
s = sy.Product(x[i], (i, 1, 100))
s_lambda = sy.lambdify(sy.DeferredVector('x'), s.doit(), 'numpy')
print(s_lambda(np.arange(101).tolist()))

打印

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Product的限制 >

解决方法 (AFAICS)变得更加复杂。如果使用调试器来跟踪代码路径 当Sum被使用时你会发现它 LambdaPrinter._print_Sum 被调用以将Sum(x[i], (i, 0, n))转换为表达式builtins.sum(x[i] for i in range(0, n+1))

如果我们向_print_ProductNumPyPrinter的子类)添加LambdaPrinter方法, 然后我们可以lambdify成功将Product转换为NumPy可以评估的表达式:

import sympy as sy
from sympy.tensor import IndexedBase, Idx
import numpy as np
import sympy.printing.lambdarepr as SPL

def _print_Product(self, expr):
    loops = (
        'for {i} in range({a}, {b}+1)'.format(
            i=self._print(i),
            a=self._print(a),
            b=self._print(b))
        for i, a, b in expr.limits)
    return '(prod([{function} {loops}]))'.format(
        function=self._print(expr.function),
        loops=' '.join(loops))
SPL.NumPyPrinter._print_Product = _print_Product

x = sy.IndexedBase('x')
i = sy.symbols('i', cls=Idx)
n = sy.symbols('n', integer=True, positive=True)
s = sy.Product(x[i], (i, 1, n))
s_lambda = sy.lambdify((sy.DeferredVector('x'), n), s, 'numpy')
print(s_lambda(np.arange(101), 5))

打印

120