使用 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
受保护的除法是一个正常的除法,但当你除以 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