SymPy 不能对产品进行lambdify [英] SymPy cannot lambdify Product
问题描述
我使用的是 SymPy 1.0 和 Python 2.7.我想计算前 100 个整数的总和:
此代码运行成功
import sympy as sy从 sympy.tensor 导入 IndexedBase, Idx将 numpy 导入为 npx = 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从 sympy.tensor 导入 IndexedBase, Idx将 numpy 导入为 npx = 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
我究竟做错了什么?是否有解决方法来获得我想要的东西?
如果我事先不知道 Product
的限制怎么办.比如说
import sympy as sy从 sympy.tensor 导入 IndexedBase, Idx将 numpy 导入为 npx = 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)
我正在努力寻找解决方法.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_fun = {产品":my_prod}f = sy.lambdify((sy.DeferredVector('x'), n), p, modules=['numpy', my_fun])f([1,2,3,4,5], 2)
问题是,当我尝试使用 Lambdified 函数 f
时,list 索引必须是整数,而不是 Symbol
错误.我想这是由于 i
这是一个符号,而它应该是一个整数.我不明白为什么在尝试调用 my_prod
之前它没有传递实际的 integer
,因为它是 Sum
案例.
当Product
的限制被提前知道
您可以通过调用 .doit()
将 Product
扩展为其组成部分来解决此问题:
In [104]: s = sy.Product(x[i], (i, 1, 10));秒输出[104]:乘积(x[i], (i, 1, 10))在 [105] 中:s.doit()输出[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从 sympy.tensor 导入 IndexedBase, Idx将 numpy 导入为 npx = 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')打印(s_lambda(np.arange(11)))
印刷品
3628800
<小时>
但是,如果您将 .doit()
与 sy.Product(x[i], (i, 1, 100))
一起使用,那么您将得到一个算术溢出,因为 np.arange(101)
具有 dtype int32
或 int64
(取决于您的操作系统)和产品 100!代码>
在 [109]: math.factorial(100)出[109]:93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
太大,无法存储在 int32
或 int64
数组值中.
在 [118]: np.iinfo('int64').max出[118]:9223372036854775807在 [119]: np.iinfo('int64').max
因此,
s = sy.Product(x[i], (i, 1, 100))s_lambda = sy.lambdify(sy.DeferredVector('x'), s.doit(), 'numpy')打印(s_lambda(np.arange(101)))
提出一个
RuntimeWarning:long_scalars 中遇到溢出
并错误地打印0
.
如果将输入从 dtype int64
数组更改为 Python int
列表,则可以正确计算乘积:
import sympy as sy从 sympy.tensor 导入 IndexedBase, Idx将 numpy 导入为 npx = 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')打印(s_lambda(np.arange(101).tolist()))
印刷品
<预> <代码> 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000<小时>
当产品
的限制不事先知道
解决办法(AFAICS) 变得更加复杂.如果使用调试器跟踪代码路径当使用 Sum
时,您会发现LambdaPrinter._print_Sum
被调用以将 Sum(x[i], (i, 0, n))
转换为表达式 builtins.sum(x[i] for我在范围内(0, n+1))
.
如果我们向 NumPyPrinter
(LambdaPrinter
的子类)添加一个 _print_Product
方法,然后我们可以得到 lambdify
成功地将 Product
转换成一个 NumPy 可以计算的表达式:
import sympy as sy从 sympy.tensor 导入 IndexedBase, Idx将 numpy 导入为 np将 sympy.printing.lambdarepr 作为 SPL 导入def _print_Product(self, expr):循环 = ('for {i} in range({a}, {b}+1)'.format(i=self._print(i),a=self._print(a),b=self._print(b))对于 expr.limits 中的 i, a, b)返回 '(prod([{function} {loops}]))'.format(函数=self._print(expr.function),loops=''.join(loops))SPL.NumPyPrinter._print_Product = _print_Productx = 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')打印(s_lambda(np.arange(101),5))
印刷品
120
I'm using SymPy 1.0 and Python 2.7. I want to compute the sum of first 100 integer numbers:
This code runs succesfully
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))
And gives 5050
as expected. But when I try to do the same with a Product
instead of a 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))
I got a NameError: global name 'Product' is not defined
What am I doing wrong? Is there a workaround to get what I want?
Edit 1:
And what if I don't know in advance the limit of the Product
. Let's say something like
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)
Edit 2:
I'm trying to find a workaround. NameError: global name 'Product' is not defined
error is given because of this:
lambdastr((sy.DeferredVector('x'), n), p)
That gives:
lambda x,n: (Product(x[i], (i, 0, n)))
While for the Sum
we got a working lambda function:
lambda x,n: ((builtins.sum(x[i] for i in range(0, n+1))))
At this point the problem revolves around the definition of the Product
function. According to the manual I can inject via a dict
my definition of a function
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)
Problem is, list indices must be integers, not Symbol
error is raised when I try to use the lambdified function f
. I guess this is due to i
that is a symbol while it is supposed to be an integer. I can't understand why it's not passed the actual integer
before trying to call my_prod
as it is for the Sum
case.
When the limits of the Product
are known in advance
You could work around the problem by calling .doit()
to expand the Product
into its component parts:
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]
For example,
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)))
prints
3628800
However, if you use .doit()
with sy.Product(x[i], (i, 1, 100))
then you'll get an arithmetic overflow since np.arange(101)
has dtype int32
or int64
(depending on your OS) and the product 100!
In [109]: math.factorial(100)
Out[109]: 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
is too large be stored in either an int32
or int64
array value.
In [118]: np.iinfo('int64').max
Out[118]: 9223372036854775807
In [119]: np.iinfo('int64').max < math.factorial(100)
Out[119]: True
Thus,
s = sy.Product(x[i], (i, 1, 100))
s_lambda = sy.lambdify(sy.DeferredVector('x'), s.doit(), 'numpy')
print(s_lambda(np.arange(101)))
raises a
RuntimeWarning: overflow encountered in long_scalars
and erroneously prints 0
.
If you change the input from an array of dtype int64
to a list of Python int
s then
the product can be computed correctly:
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()))
prints
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
When the limits of the Product
are not known in advance
The work-around
(AFAICS) becomes more complicated. If you use a debugger to trace the code path
followed when Sum
is used you'll find that
LambdaPrinter._print_Sum
is called to convert Sum(x[i], (i, 0, n))
to the expression builtins.sum(x[i] for
i in range(0, n+1))
.
If we add a _print_Product
method to NumPyPrinter
(a subclass of LambdaPrinter
),
then we can get lambdify
to successfully convert Product
into an expression that NumPy can evaluate:
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))
prints
120
这篇关于SymPy 不能对产品进行lambdify的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!