强制计算Sympy中的乘法
在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
计算出结果呢?我尝试了各种simplify
、expand
等的组合,想了所有可能的选项,但没有任何效果。我甚至可以用.args[0]
提取出和的前半部分,试着简化或展开它。结果还是没有!
更糟糕的是,我实际使用的情况涉及很多嵌套的表达式,比如
>>> b*expand((x+y)*a)
b*(x*a + y*a)
到底发生了什么?我该如何让它工作?有什么魔法词吗?
2 个回答
在Python中,Mul这个东西并不会调用__mul__
这个方法,只有当你用到*
这个符号时,__mul__
才会被调用,这就是Python的运算符重载。现在,SymPy这个库还没有提供处理Mul的办法,不过这在我们的待办事项列表上。主要的问题是我们还不太确定该怎么做。你的解决方案可能是最好的方法(除了我提到的那个修正)。
你也可以尝试设置_op_priority
(可以在SymPy的代码库里搜索一下,看看怎么使用这个)。
嗯,这不是我最初希望的结果,但我觉得解决方案肯定需要逐步分析这个表达式,并强制执行一些操作。执行这些操作就像使用 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
,这个我会在其他地方处理。