【问题标题】:Force evaluation of Mul(tiplication) in sympy在 sympy 中对 Mul(tiplication) 进行强制评估
【发布时间】:2014-03-28 03:51:38
【问题描述】:

在 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

为什么这些不被吞噬?!结果看起来和我在上一个提示中输入的完全一样,但是什么也没发生。

Gobblers 现在是 Muls 中的因子,其中两个是 Add 中的术语。那么我怎样才能让那些Muls 评估呢?我已经尝试了simplifyexpand 等的所有组合,以及我能想到的所有选项组合。但没有什么能让这些去任何地方。我什至可以用.args[0] 提取总和的前半部分并尝试简化/扩展它。没什么!

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

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

发生了什么事?我怎样才能让它工作?神奇的词是什么?

【问题讨论】:

    标签: python sympy


    【解决方案1】:

    Mul 不调用 __mul____mul__ 仅在使用 * 时调用,因为它是 Python 的运算符重载)。 SymPy 目前不提供在 Mul 上调度的方法,尽管它在我们的 TODO 列表中。阻碍它的主要因素是我们不确定如何去做。您的解决方案可能是最好的方法(除了我提到的更正)。

    您也可以设置_op_priority(搜索 SymPy 代码库以了解如何使用它)。

    【讨论】:

    • 是的,我认为必须明确地执行gobbleExpr 之类的操作是可以接受的,就像simplify 一样。但是最好有某种挂钩到simplify 来完成这项工作——所以我的类中的一些方法simplify 会查看并简化包含我的对象的任何节点作为args 之一,然后将其传播到树上。 (也许有这样的事情,我只是不知道。)
    • 顺便说一句,我设置了_op_priority各种合理和荒谬的值,但它从未改变任何东西。
    • 您可以定义一个_eval_simplify 函数,该函数将由simplify 在您的对象上调用。这可以让对象简化自身,但不一定与表达式的其余部分相关,因此需要更多考虑。
    【解决方案2】:

    嗯,这不是我所希望的,但我认为解决方案必然涉及通过表达式并强制应用程序。应用这样的应用程序就像应用simplify,所以它不会太意外或太繁重。对于我的目的,以下内容就足够了,尽管更一般的表达式可能需要更多的特殊情况。基本思想在here, in the tutorial 中找到,涉及到表达式树的递归:

    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)
    

    所以剩下的只有 Gobblers 相互添加,我在其他地方处理。

    【讨论】:

    • 你应该只使用一个简单的else: return expr.func(*[GobbleExpr(i) for i in expr.args])
    • 你的意思是如果不是 Gobbler 或 Symbol 的实例?这对我没有任何作用;结果只是未评估 Muls 等。
    • 您的代码只处理 Gobbler、Mul 和 Add。 else 将使它处理所有事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 1970-01-01
    • 1970-01-01
    • 2016-06-28
    相关资源
    最近更新 更多