如何将可调用函数添加到 Sympy 表达式
How to add a callable function to a Sympy expression
如果我有一个表达式 x = Symbol('x')
和 f1=x**2
,我想进一步添加一些 f2
,其中 f2 = interp1d(t, y)
是 scipy 插值。如何将 f2
变成一个表达式,使我有类似 f = x**2 + f2(x)
的东西,以便我以后可以将 f
计算为 f.subs(x, some_number)
?
由于代码规范,我无法单独评估 f1
和 f2
然后添加结果数字,我需要能够将其添加到现有的 sympy 表达式中并且使用 .subs()
之类的东西对其进行评估
一种非常冒险的方法是为您的数值函数创建一个包装对象,如下所示:
from sympy import *
import numpy as np
var("x")
# symbolic expression
f1 = cos(x)
# numerical function
f2 = lambda t: np.sin(t)
class MyFunc(Expr):
"""Create a symbolic wrapper to a numerical function."""
def __new__(cls, arg, **kwargs):
obj = Expr.__new__(cls, **kwargs)
obj._custom_func = arg
return obj
def _subs(self, old, new, **hints):
return self._custom_func(float(new))
expr = f1 + MyFunc(f2)
expr.subs(x, np.pi/4)
# out: 1.41421356237309
一种方法,但它需要 hard-coding 在 class:
中调用的函数
f2 = lambda t: np.sin(t)
class MyFunc(Function):
@classmethod
def eval(cls, arg):
arg = sympify(arg, strict=True)
if arg.is_Number:
return sympify(f2(float(arg)), strict=True)
更像 Davide 的回答,但有一些修正:
class FuncWrapper(Symbol):
"""Wraps a python callable as a Basic instance"""
def __new__(cls, func, name):
obj = super().__new__(cls, name)
obj._wrapped = func
return obj
@property
def wrapped(self):
return self._wrapped
def _hashable_content(self):
return (self.wrapped,) # needed for __eq__
def eval(self, arg):
if arg.is_Number:
return sympify(self.wrapped(float(arg)))
def __call__(self, arg):
return Call(self, arg)
class Call(Function):
@classmethod
def eval(cls, func, arg):
arg = sympify(arg)
result = func.eval(arg)
if result is not None:
return result
这样你就有了:
In [61]: f = FuncWrapper(np.sin, 'f')
In [62]: x + f(x)
Out[62]: x + Call(f, x)
In [63]: _.subs(x, 1)
Out[63]: 1.84147098480790
如果我有一个表达式 x = Symbol('x')
和 f1=x**2
,我想进一步添加一些 f2
,其中 f2 = interp1d(t, y)
是 scipy 插值。如何将 f2
变成一个表达式,使我有类似 f = x**2 + f2(x)
的东西,以便我以后可以将 f
计算为 f.subs(x, some_number)
?
由于代码规范,我无法单独评估 f1
和 f2
然后添加结果数字,我需要能够将其添加到现有的 sympy 表达式中并且使用 .subs()
一种非常冒险的方法是为您的数值函数创建一个包装对象,如下所示:
from sympy import *
import numpy as np
var("x")
# symbolic expression
f1 = cos(x)
# numerical function
f2 = lambda t: np.sin(t)
class MyFunc(Expr):
"""Create a symbolic wrapper to a numerical function."""
def __new__(cls, arg, **kwargs):
obj = Expr.__new__(cls, **kwargs)
obj._custom_func = arg
return obj
def _subs(self, old, new, **hints):
return self._custom_func(float(new))
expr = f1 + MyFunc(f2)
expr.subs(x, np.pi/4)
# out: 1.41421356237309
一种方法,但它需要 hard-coding 在 class:
中调用的函数f2 = lambda t: np.sin(t)
class MyFunc(Function):
@classmethod
def eval(cls, arg):
arg = sympify(arg, strict=True)
if arg.is_Number:
return sympify(f2(float(arg)), strict=True)
更像 Davide 的回答,但有一些修正:
class FuncWrapper(Symbol):
"""Wraps a python callable as a Basic instance"""
def __new__(cls, func, name):
obj = super().__new__(cls, name)
obj._wrapped = func
return obj
@property
def wrapped(self):
return self._wrapped
def _hashable_content(self):
return (self.wrapped,) # needed for __eq__
def eval(self, arg):
if arg.is_Number:
return sympify(self.wrapped(float(arg)))
def __call__(self, arg):
return Call(self, arg)
class Call(Function):
@classmethod
def eval(cls, func, arg):
arg = sympify(arg)
result = func.eval(arg)
if result is not None:
return result
这样你就有了:
In [61]: f = FuncWrapper(np.sin, 'f')
In [62]: x + f(x)
Out[62]: x + Call(f, x)
In [63]: _.subs(x, 1)
Out[63]: 1.84147098480790