SymPy 不能对产品进行lambdify [英] SymPy cannot lambdify Product

查看:18
本文介绍了SymPy 不能对产品进行lambdify的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 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 int32int64(取决于您的操作系统)和产品 100!

在 [109]: math.factorial(100)出[109]:93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

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

在 [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 ints 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆