我想定义遵循以下数学定义的一类函数:
定义
给定一个函数q(t)和两个值a和b,我们将以下函数定义为变量
/
| q(t) if s=0 or t=a or t=b
v(t,s) = |
| v(t,s) else
\
我正在尝试使用
Function
子类模拟此行为定义: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)
但是它有同样的问题。
最佳答案
我认为您需要重塑此操作的方式。
创建您的Var
函数。
您确实希望示例中的Var
是Function
类。 sympy
围绕作为类的函数进行设计,并使用eval()
类方法对其进行求值。覆盖__call__
子类的Function
似乎是非常不规范的,而且我还没有看到任何使用该功能的sympy
内置函数,因此我认为这不是正确的方法。一种方法是创建一个工厂函数来为您创建类:
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()
行为的问题...我不太了解您想要什么:
有很好的指南在python上构造任意数学函数吗?
我认为
sympy
很好,并且在代码库中有合理的文档和许多内置示例,很容易从中获取。无论如何,要寻求指导的都是关于stackoverflow ;-)的off-topic (see point 4)。关于python - 在sympy中自定义Function子类的subs()功能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43197704/