在 sympy 中自定义 Function 子类的 subs() 功能
Customising the subs() functionality of Function subclasses in sympy
我想定义一个 class 函数,遵循这个数学定义:
定义
给定函数 q(t) 和两个值 a 和 b,我们将以下函数定义为变体
/
| q(t) if s=0 or t=a or t=b
v(t,s) = |
| v(t,s) else
\
我正在尝试使用 Function
subclass 来模拟此行为定义:
from sympy import Function
class Variation(Function):
# Initialize the function with the desired properties
def __init__(self, path, st, en, name='\vartheta'):
self.path = path
self.st = st
self.en= en
self.ends = [self.st, self.en]
self.name = name
# here I define the behaviour when called
def __call__(self, tt, ss):
if tt in self.ends:
return self.path(tt)
elif ss == 0:
return self.path(tt)
else:
return Function(self.name)(tt,ss) # This is the part that fails to behave
该函数在调用时表现良好:
from sympy import *
s,t,a,b = symbols('s t a b')
c = Function('c')
Var = Variation(c, a, b)
Var(t,s), Var(a,s), Var(t,0)
> \vartheta(t,s), q(a), q(t)
但正如预期的那样,如果我们这样做:
Var(t,s).subs(t,0)
> \vartheta(t,0)
有没有办法修改 .subs()
的方法行为?因为据我所知,integrate()
函数使用了 subs()
。
我也尝试将 Function(self.name)(tt,ss)
更改为 self(tt,ss)
但这给了我一个无限循环(也是预期的)。
同理,是否有在 python 上构建任意数学函数的良好指南?
编辑:已尝试
def Var(t,s):
return Piecewise((c(t), s==0), (c(t), t==a), (c(t), t==b), (Function('v')(t,s), True ))
Var(t,s).subs(t,0)
但它有同样的问题。
这还不是问题的答案。但据我了解,这个问题需要一些翻新。
使用post中定义的Variation
class,我将c
修改为q
只是为了符合提供的函数定义,只是为了检查我是否正确理解了问题:
from sympy import *
s,t,a,b = symbols('s t a b')
q = Function('q')
Var = Variation(q, a, b)
Var(t,s), Var(a,s), Var(t,0)
(\vartheta(t, s), q(a), q(t))
符合预期:
Var(t,s).subs(t,0)
\vartheta(0, s)
即 Var(0,s)
,因为 t
与 a
和 b
不同,并且 s
不为零。
我认为您需要稍微调整一下处理方式。
正在创建您的 Var
函数。
您确实希望示例中的 Var
成为 Function
class。 sympy
是围绕 classes 函数设计的,并使用 eval()
class 方法来计算它们。覆盖 Function
subclass 的 __call__
似乎非常不标准,我还没有看到任何使用它的 sympy
内置函数,所以我不不认为这是正确的方法。一种方法是创建一个工厂函数来为您创建 class:
def Variation(path_, st_, en_, v_):
class Variation(Function):
nargs = 2
path = path_
st = st_
en = en_
ends = [st, en]
v = v_
@classmethod
def eval(cls, tt, ss):
if tt in cls.ends:
return cls.path(tt)
if ss == 0:
return cls.path(tt)
return cls.v(tt, ss)
return Variation
Var = Variation(c, a, b, Function(r'\vartheta'))
- 我已经用实际函数替换了你的
'name'
变量,这看起来更明智。
现在您可以使用标准标志创建变体并防止立即求值(如果需要):
# by default the function is immediately evaluated...
Var(a, t)
>>> c(a)
# ...but that can be overridden
Var(a, t, evaluate=False)
>>> Variation(a, t)
您也可以通过展平 Var
函数并将 st
en
和 path
参数直接传递给 eval()
来解决这个问题工厂功能层:
class Variation(Function):
@classmethod
def eval(cls, path, st, en, v, tt, ss):
if tt in [st, en]:
return path(tt)
elif ss == 0:
return path(tt)
else:
return v(tt, ss)
Variation(c, a, b, Function(r'\vartheta'), a, t)
>>> Variation(c, a, b, \vartheta, a, t)
请注意,由于您可以覆盖 .eval()
,因此您可以对其进行修改,使其不会根据需要自动简化,而只是返回 cls
:
的新实例
class Variation(Function):
no_eval = True
@classmethod
def eval(cls, tt, ss):
if cls.no_eval:
return cls(tt, ss, evaluate=False)
# etc.
自定义 .subs()
默认情况下,每当您执行 subs()
时,sympy 也会执行 eval()
(根据 the .subs()
docs)。因此,默认情况下,当您使用您的特殊值之一执行 .subs()
时,将调用 .eval()
并简化函数。
但是,您现在可以覆盖 ._eval_subs()
并执行您自己的操作,如果您愿意:
class Variation(Function):
...
def _eval_subs(self, old, new):
# return self to do no substitutions at all
return self
# return None to continue normally by next calling _subs on the arguments to this Function
# return some other Expression to return that instead.
请注意,._eval_subs()
返回的任何内容随后也会被 .eval()
编辑。如果你想解决这个问题,你可以按照上面的解释覆盖 .eval()
。
所以我认为这回答了关于如何修改 .subs()
行为的问题...
不太明白你想用什么:
is there a good guide to construct arbitrary mathematical functions on python?
我认为 sympy
非常好,并且在其代码库中有合理的文档和许多内置示例,很容易从中借鉴。无论如何,在 Whosebug 上寻求指导是 off-topic (see point 4) ;-).
我想定义一个 class 函数,遵循这个数学定义:
定义 给定函数 q(t) 和两个值 a 和 b,我们将以下函数定义为变体
/
| q(t) if s=0 or t=a or t=b
v(t,s) = |
| v(t,s) else
\
我正在尝试使用 Function
subclass 来模拟此行为定义:
from sympy import Function
class Variation(Function):
# Initialize the function with the desired properties
def __init__(self, path, st, en, name='\vartheta'):
self.path = path
self.st = st
self.en= en
self.ends = [self.st, self.en]
self.name = name
# here I define the behaviour when called
def __call__(self, tt, ss):
if tt in self.ends:
return self.path(tt)
elif ss == 0:
return self.path(tt)
else:
return Function(self.name)(tt,ss) # This is the part that fails to behave
该函数在调用时表现良好:
from sympy import *
s,t,a,b = symbols('s t a b')
c = Function('c')
Var = Variation(c, a, b)
Var(t,s), Var(a,s), Var(t,0)
> \vartheta(t,s), q(a), q(t)
但正如预期的那样,如果我们这样做:
Var(t,s).subs(t,0)
> \vartheta(t,0)
有没有办法修改 .subs()
的方法行为?因为据我所知,integrate()
函数使用了 subs()
。
我也尝试将 Function(self.name)(tt,ss)
更改为 self(tt,ss)
但这给了我一个无限循环(也是预期的)。
同理,是否有在 python 上构建任意数学函数的良好指南?
编辑:已尝试
def Var(t,s):
return Piecewise((c(t), s==0), (c(t), t==a), (c(t), t==b), (Function('v')(t,s), True ))
Var(t,s).subs(t,0)
但它有同样的问题。
这还不是问题的答案。但据我了解,这个问题需要一些翻新。
使用post中定义的Variation
class,我将c
修改为q
只是为了符合提供的函数定义,只是为了检查我是否正确理解了问题:
from sympy import *
s,t,a,b = symbols('s t a b')
q = Function('q')
Var = Variation(q, a, b)
Var(t,s), Var(a,s), Var(t,0)
(\vartheta(t, s), q(a), q(t))
符合预期:
Var(t,s).subs(t,0)
\vartheta(0, s)
即 Var(0,s)
,因为 t
与 a
和 b
不同,并且 s
不为零。
我认为您需要稍微调整一下处理方式。
正在创建您的 Var
函数。
您确实希望示例中的 Var
成为 Function
class。 sympy
是围绕 classes 函数设计的,并使用 eval()
class 方法来计算它们。覆盖 Function
subclass 的 __call__
似乎非常不标准,我还没有看到任何使用它的 sympy
内置函数,所以我不不认为这是正确的方法。一种方法是创建一个工厂函数来为您创建 class:
def Variation(path_, st_, en_, v_):
class Variation(Function):
nargs = 2
path = path_
st = st_
en = en_
ends = [st, en]
v = v_
@classmethod
def eval(cls, tt, ss):
if tt in cls.ends:
return cls.path(tt)
if ss == 0:
return cls.path(tt)
return cls.v(tt, ss)
return Variation
Var = Variation(c, a, b, Function(r'\vartheta'))
- 我已经用实际函数替换了你的
'name'
变量,这看起来更明智。
现在您可以使用标准标志创建变体并防止立即求值(如果需要):
# by default the function is immediately evaluated...
Var(a, t)
>>> c(a)
# ...but that can be overridden
Var(a, t, evaluate=False)
>>> Variation(a, t)
您也可以通过展平 Var
函数并将 st
en
和 path
参数直接传递给 eval()
来解决这个问题工厂功能层:
class Variation(Function):
@classmethod
def eval(cls, path, st, en, v, tt, ss):
if tt in [st, en]:
return path(tt)
elif ss == 0:
return path(tt)
else:
return v(tt, ss)
Variation(c, a, b, Function(r'\vartheta'), a, t)
>>> Variation(c, a, b, \vartheta, a, t)
请注意,由于您可以覆盖 .eval()
,因此您可以对其进行修改,使其不会根据需要自动简化,而只是返回 cls
:
class Variation(Function):
no_eval = True
@classmethod
def eval(cls, tt, ss):
if cls.no_eval:
return cls(tt, ss, evaluate=False)
# etc.
自定义 .subs()
默认情况下,每当您执行 subs()
时,sympy 也会执行 eval()
(根据 the .subs()
docs)。因此,默认情况下,当您使用您的特殊值之一执行 .subs()
时,将调用 .eval()
并简化函数。
但是,您现在可以覆盖 ._eval_subs()
并执行您自己的操作,如果您愿意:
class Variation(Function):
...
def _eval_subs(self, old, new):
# return self to do no substitutions at all
return self
# return None to continue normally by next calling _subs on the arguments to this Function
# return some other Expression to return that instead.
请注意,._eval_subs()
返回的任何内容随后也会被 .eval()
编辑。如果你想解决这个问题,你可以按照上面的解释覆盖 .eval()
。
所以我认为这回答了关于如何修改 .subs()
行为的问题...
不太明白你想用什么:
is there a good guide to construct arbitrary mathematical functions on python?
我认为 sympy
非常好,并且在其代码库中有合理的文档和许多内置示例,很容易从中借鉴。无论如何,在 Whosebug 上寻求指导是 off-topic (see point 4) ;-).