我想定义遵循以下数学定义的一类函数:

定义
给定一个函数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函数。

您确实希望示例中的VarFunction类。 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 enpath参数直接传递到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/

10-12 06:45