强制计算Sympy中的乘法

1 投票
2 回答
1027 浏览
提问于 2025-04-18 00:16

在sympy中,乘法在加法上分配似乎没有计算乘法的结果。

我创建了一个sympy.Symbol的子类,它知道如何和其他东西相乘。为了简单起见,我们假装这个子类会吞掉任何和它相乘的东西:

from sympy import *

class Gobbler(Symbol):
    _op_priority = 1.0e200
    def __mul__(self, other):
        return Gobbler('gob('+self.name+'*'+str(other)+')')
    def __rmul__(self, other):
        return Gobbler('gob('+self.name+'*'+str(other)+')')

x = Gobbler('x')
y = Gobbler('y')
a = Symbol('a')
b = Symbol('b')

[是的,那个_op_priority确实很奇怪。不过把它改成更合理的数字,比如10.0,也没有改变什么。] 我可以运行

>>> x*a
gob(x*a)
>>> x*a + y*a
gob(x*a) + gob(y*a)

一切都很顺利,直到我遇到

>>> expand((x+y)*a)
x*a + y*a

为什么这些没有被吞掉呢?!结果看起来和我在之前输入的一模一样,但什么都没发生。

这些“吞噬者”现在是Mul中的因子,其中两个是Add中的项。那么我该如何让这些Mul计算出结果呢?我尝试了各种simplifyexpand等的组合,想了所有可能的选项,但没有任何效果。我甚至可以用.args[0]提取出和的前半部分,试着简化或展开它。结果还是没有!

更糟糕的是,我实际使用的情况涉及很多嵌套的表达式,比如

>>> b*expand((x+y)*a)
b*(x*a + y*a)

到底发生了什么?我该如何让它工作?有什么魔法词吗?

2 个回答

0

在Python中,Mul这个东西并不会调用__mul__这个方法,只有当你用到*这个符号时,__mul__才会被调用,这就是Python的运算符重载。现在,SymPy这个库还没有提供处理Mul的办法,不过这在我们的待办事项列表上。主要的问题是我们还不太确定该怎么做。你的解决方案可能是最好的方法(除了我提到的那个修正)。

你也可以尝试设置_op_priority(可以在SymPy的代码库里搜索一下,看看怎么使用这个)。

0

嗯,这不是我最初希望的结果,但我觉得解决方案肯定需要逐步分析这个表达式,并强制执行一些操作。执行这些操作就像使用 simplify 一样,所以这并不太意外,也不会太麻烦。下面的内容对我来说已经足够了,不过更复杂的表达式可能需要处理更多特殊情况。基本的思路可以在这个教程中找到,主要是通过表达式树进行递归处理:

def gobbleExpr(expr):
    if isinstance(expr, Gobbler):
        return expr
    if isinstance(expr, Mul):
        args = list(o if o.is_Atom or isinstance(o, Gobbler)
                    else gobbleExpr(o)
                    for o in expr.args)
        gobbler = prod(t for t in args if isinstance(t, Gobbler))
        others = prod(o for o in args if not isinstance(o, Gobbler))
        if gobbler==1:
            return others
        else:
            return gobbler.__mul__(others)
    if isinstance(expr, Add):
        return sum(gobbleExpr(arg) for arg in expr.args)
    return expr

所以现在我可以做一些类似这样的事情

>>> expand(b*(x+y)*a)
x*a*b + y*a*b
>>> gobbleExpr(_)
gob(gob(x*1)*a*b) + gob(gob(y*1)*a*b)

所以剩下的就是相互叠加的 Gobbler,这个我会在其他地方处理。

撰写回答