【问题标题】:sympy: Using Wild symbols as input for functionsympy:使用 Wild 符号作为函数的输入
【发布时间】:2016-06-15 12:55:33
【问题描述】:

考虑python/sympy中的以下表达式:

from sympy.abc import x, y
expression = 3*x**2*y**1 + x**2*y**3

现在我想通过使用模式匹配将x**n*y**m 替换为max(n,m)

from sympy import Wild
n = Wild('n')
m = Wild('m')
expression = expression.replace(x**n*y**m,max(n,m))

但是,我收到了TypeError

Traceback (most recent call last):
   File "wild.py", line 15, in <module>
     expression = expression.replace(x**n*y**m,max(n,m))
   File "/usr/lib/python3/dist-packages/sympy/core/relational.py", line 103, in __nonzero__
     raise TypeError("cannot determine truth value of\n%s" % self)
TypeError: cannot determine truth value of
    m_ > n_

问题显然是在匹配表达式时,sympy 不会在将 Wild 符号的值转发到 max-函数之前将其转换为匹配的值。有什么办法可以做到吗?

请注意,这是我遇到的更普遍问题的一个简单示例,因此不能很好地概括的解决方法不是很有帮助。特别希望有使用表达式匹配的解决方案。

更新:按照 Sandwich 的建议,将 max(n,m) 替换为 (m+n+abs(m-n))/2 是可行的,但是,我在实际程序中使用的函数要复杂得多。 关于三明治提到的问题,replace 从表达式树的底部开始执行替换:如果我使用exact=True 并定义f = sympy.Function('f'),则以下工作(除了我将不得不单独处理某些情况):

expression = expression.replace(x**n*y**m,f(n,m),exact=True)

但是,它仍然不适用于max(n,m)

【问题讨论】:

  • 用数学上等效的 (m+n+abs(m-n))/2 替换 max(m,n) 有助于解决您遇到的错误。但是还有一个更大的问题,replace 从表达式树的底部开始执行替换。因此,x**2*y**3 将替换为 1,因为它是 x*x*y*y*y,其中五个术语中的每一个都替换为 1。
  • @soup x**2*y**3 表示为 Mul(Pow(x, 2), Pow(y, 3)),而不是 Mul(x, x, y, y, y)。实际上,表示后者的唯一方法是使用evaluate=False 显式创建它。

标签: python python-3.x sympy


【解决方案1】:

max 是内置的 Python 函数,它会尝试立即计算,因此会出现错误(当 nm 是符号时,它无法确定哪个更大)。您可能需要 SymPy Max 函数,它象征性地工作。您在答案中定义的my_func 实际上是Max 的基本实现。

In [14]: expression = expression.replace(x**n*y**m, Max(n,m))

In [15]: expression
Out[15]: 4

这似乎不起作用(答案 4 是错误的)。问题是它与x**2 匹配为x**2*y**0。由于替换函数的数学值取决于表达式的形式,这是有问题的,因为 SymPy 试图变得聪明。您实际上可以使nmWild('n', exclude=[x, 0]) 不匹配0,但是存在一个问题,即它与x**2*y 不匹配x**2*y**1

因此,我建议将您的表达式转换为多项式并准确地进行替换。希望这可以很好地概括您实际在做什么

In [18]: Poly(expression, x, y)
Out[18]: Poly(x**2*y**3 + 3*x**2*y, x, y, domain='ZZ')

In [19]: Poly(expression, x, y).terms()
Out[19]: [((2, 3), 1), ((2, 1), 3)]

In [20]: sum(max(pows)*coeff for pows, coeff in Poly(expression, x, y).terms())
Out[20]: 9

【讨论】:

  • 谢谢,但是这个答案在几个层面上都有问题:首先它给出了错误的答案:通过手动计算可以很容易地检查结果不应该是 4,而是应该是 9。这个错误来自表达式被3*Max(1,0)^2*Max(0,1)^2 + Max(1,0)^2*Max(0,2)^3 替换的事实,这可以通过使用虚拟函数很容易看出。使用标志 exact=True 会更好一些,但是,在这种情况下,表达式的 3*x**2*y**1 部分不会被捕获。最后,正如我之前所说,我希望能够使用比max 更复杂的功能...
  • ...(续)解决所有这些问题导致我的答案。我可能知道如何改进我的答案以缩短执行时间,如果我有时间我会对此进行调查。
  • @kalix 我明白了。真正的问题是x**n*y**m -&gt; Max(n, m) 在数学意义上没有明确定义。我使用Poly 使用更直接的解决方案更新了答案。
  • 是的,这很好用,而且比我的解决方案快得多。
【解决方案2】:

好的,我找到了一个解决方案,虽然这可能与我对编写与 sympy 很好交互的函数知之甚少有关。

首先我导入所有相关的内容:

import sympy
from sympy.abc import x, y, a, b,z
from sympy import Wild
from sympy import Function

然后我定义我自己的函数来返回最大值,这里返回的 max(x,y) 可以被一个更复杂的函数代替:

class my_func(Function):
  @classmethod
  def eval(cls, x, y):
    if x.is_Number and y.is_Number:
      return max(x,y)

我要修改的表达式是:

3*x**2*y**1 + x**2*y**3

我定义了必要的通配符和一个 sympy 函数,该函数用于临时进行替换而不进行评估:

n = Wild('n')
m = Wild('m')
k = Wild('k')
f = Function('f')
expression = expression.replace(x,f(1,0),exact=True)
expression = expression.replace(y,f(0,1),exact=True)
expression = expression.replace(f(1,0)**n,f(n,0),exact=True)
expression = expression.replace(f(0,1)**n,f(0,n),exact=True)
expression = expression.replace(k*f(0,m)*f(n,0),k*f(n,m),exact=True)

这些替换可以解决我的问题中出现的所有情况。在最后一步中,我将 f 替换为进行评估的 my_func

expression = expression.replace(f(n,m),my_func(n,m))

也许有人会找到更好的解决方案....

【讨论】:

    猜你喜欢
    • 2017-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    相关资源
    最近更新 更多