使用 Sympy 的受保护部门

Protected Division Using Sympy

受保护的除法是一个正常的除法,但当你除以 0 时,它 returns 一个固定常数(通常为 1)。

def protected_div(x, y):
    if y == 0:
        return 1
    return x/y

有没有办法将其用作 sympy 上的运算符(例如替换标准除法)?

这是我想要的示例:

>>> import sympy as sym
>>> x = sym.Symbol('x')
>>> expr = 1/x #(protected division goes here?)
>>> expr.subs(x, 0)
1

评估时必须保护部门。

编辑 1:

我尝试过的:

1。 将 sym.lambidify 与模块参数集一起使用:

>>> x = sym.Symbol('x')
>>> expr = 1/x
>>> lamb = sym.lambdify(x, expr, modules={'/':protected_div})
>>> print(lamb(0))
ZeroDivisionError: 0.0 cannot be raised to a negative power

这不起作用,因为 sympy 在 lambidifying 时将 1/x 转换为 x**(-1)。我尝试覆盖电源运算符,但我不知道函数名称。我试过 'Pow'、'pow'、'**' 和 none。

但是,如果我将表达式声明为 expr = 1.0/x,它实际上不会转换为负幂,但它不会使用我的自定义除法函数。我认为使用 module 参数无法覆盖这些类型的函数。

2。 @Zaz 建议:

class floatsafe(float):
    def __truediv__(self, __x):
        if __x == 0:
            return floatsafe(1)
        return super().__truediv__(__x)

x = sym.Symbol('x')
expr = floatsafe(1)/x
print(expr.subs(x, floatsafe(0)))

Returns

zoo

复无穷大。

我尝试将此方法与 sym.lambdify 结合使用,但在我对函数进行 lambdify 后,股息被转换为浮点数。

如果股息是可变的,它也不起作用:

x = sym.Symbol('x')
expr = x/0.0
a = sym.lambdify(x, expr, modules={'/':floatsafe.__truediv__})
print(inspect.getsource(a))
print(a(floatsafe(0)))

产出

def _lambdifygenerated(x):
    return nan*x

nan

编辑:似乎有些人不明白我为什么要这样做。它适用于使用 sympy 的遗传编程算法。保护除法是GP中的常用运算符,因此创建的解是有效的。

我们在day-to-day上使用的正则数学是实数集上的环,ℝ:环的性质是你有两个运算(例如乘法和加法)和一个它们(例如加法)总是会在集合中产生另一个数字。

您可以通过删除 0 或将集合扩展到超实数来创建更具体的字段概念(这样两个操作将始终产生集合中的另一个成员)。

我的意思是,在不知道您要解决的问题到底是什么的情况下,我猜想与其重新定义除法,不如重新定义您正在使用的数字系统更有意义:无论出于何种原因,您有一些数字系统在除以零时应该 return 1,那么为什么不创建一个 float 的子类,例如?

class floatD01(float):
    def __truediv__(self, divisor):
        if divisor == 0:
            return 1
        return self/divisor

您可能还想扫描 help(float) 以查找您可能想要更改的与除法相关的任何其他方法,例如 __divmod____floordiv__7//3 == 2)、等,并仔细考虑您希望您创建的这个新数学组如何工作以及为什么。

其他可能更强大的选项是核化并尝试捕获所有 ZeroDivisionError 并将它们替换为一个(通过 )或在您使用的任何代码中 运行 或者,如果合适的话,实现类似语言 R 广泛使用的东西:NA 值。我确定有一些方法(我相信numpy)可以按照以下方式做一些事情:C = [1/3, 2/2, 3/1, 4/0] # == [1/3, 2/2, 3/1, NA] sum(C) = 4.333

解决方案实际上非常简单,尽管我实际上无法重载除法运算符,我所要做的就是为受保护的除法创建一个 sympy 函数并使用它。

class protected_division(sym.Function):
    @classmethod
    def eval(cls, x, y):
        if y.is_Number:
            if y.is_zero:
                return sym.S.One
            else:
                return x/y

然后在表达式中使用它:

>>> expr = protected_division(1, sym.Symbol('x'))
protected_division(1, x)
>>> expr.subs(sym.Symbol('x'), 0)
1
>>> expr.subs(sym.Symbol('x'), 3)
1/3

我没有找到如何让 class 告诉 sym.lambdify 在“lambdification”的情况下该怎么做,但您可以为此使用 modules 参数:

>>> def pd(x, y):
...     if y == 0:
...         return 1
...     return x/y
... 
>>> l = sym.lambdify(sym.Symbol('x'), expr, modules={'protected_division': pd})
>>> l(3)
1.6666666666666667
>>> l(0)
1