Python: 解析相互依赖的数学函数
Python: Parsing math functions that are dependent on each other
我有一个对象列表,每个对象都是一个数学函数,这些函数可能相互依赖,例如:
[
Formula("G", ["y"], "1 - y"),
Formula("V", ["x", "y"], "G(y) * x / 3"),
Formula("U", ["x", "y"],"(G(y)**2) / (9 * V(x, y)) + V(x, y)")
]
其中第一个参数是函数名,第二个是使用的变量列表,第三个是字符串 - 函数的表达式。
有没有一种简单的方法来评估函数 U(x, y) 在给定点的值,例如在 [2, 3] 并递归调用 G(3)和V(2, 3),得到最后的结果?
我曾尝试在 Sympy 中执行此操作,但无法在函数 V(x,y)[ 中调用例如函数 G(y) =25=]
也许是这样的?
>>> def Formula(*args):
... return parse_expr('{%s(*%s): %s}' % args)
...
>>> f =[
... Formula("G", ["y"], "1 - y"),
... Formula("V", ["x", "y"], "G(y) * x / 3"),
... Formula("U", ["x", "y"],"(G(y)**2) / (9 * V(x, y)) + V(x, y)")
... ]
>>> f
[{G(y): 1 - y}, {V(x, y): x*G(y)/3}, {U(x, y): G(y)**2/(9*V(x, y)) + V(x, y)}]
f
已按拓扑排序,因此返回替换
>>> from sympy import Dict
>>> e=Dict(f[-1])
>>> e=e.subs(f[-2])
>>> e=e.subs(f[-3])
>>> a,b=dict(e).popitem()
>>> U = Lambda(a.args,b)
>>> U(2,3)
-5/3
如果它没有排序,你可以使用 topological_sort
,也许通过 repsort
here 这样做:
>>> repsort(*[tuple(i.items()) for i in f])
[(U(x, y), G(y)**2/(9*V(x, y)) + V(x, y)), (V(x, y), x*G(y)/3), (G(y), 1 - y)]
>>> s = _
>>> expr = s[0][0]
>>> for i in s:
... expr = expr.subs(*i)
...
>>> expr
x*(1 - y)/3 + (1 - y)/(3*x)
>>> U = Lambda(tuple(ordered(expr.free_symbols)), _)
>>> U(2,3)
-5/3
感谢您的所有建议。在我给出的示例中,Formula
是按拓扑顺序排列的,但实际上并不总是这样。
我可以使用@Stef 的解决方案,但我必须格式化公式表达式,然后 eval()
它
x,y = sympy.symbols('x y'); G = 1-y; V = G * x / 3; U = G**2 / (9*V+V)
然后@OscarBenjamin 建议使用 sympy 的 parse_expr
,效果很好,直到我意识到公式并不总是按拓扑顺序给出。所以我发现,尝试按拓扑顺序排列然后解析它会花费太多执行时间。
最终,我决定制作自己的解析器,它看起来像这样(测试 类 和变量):
import re
import copy
class Formula():
function_name = ""
variables = []
expression = ""
__expr__ = ""
other_func_calls = 0
def __init__(self, function_name:str, variables:list, fun:str) -> None:
self.function_name = function_name
self.variables = variables
self.expression = fun
other_func = []
for i in fun:
if ord(i) in range(ord("A"), ord("Z") + 1):
self.other_func_calls += 1
other_func.append(i)
self.__expr__ = re.sub('[A-Z]?\((\w|, )*\)','_TBR_', fun) # _TBR_ is being replaced later
self.other_func = other_func # list of other functions in chronological order
class Pyramid():
name:str
functions:dict[str:Formula]
def __init__(self, name:str, funs:dict[str:Formula]) -> None:
self.name = name
self.functions = funs
def get_result(self, fun:str, values:dict[str:int]):
if (self.functions[fun].other_func_calls == 0): # Function does not call other functions
return eval(self.functions[fun].expression, values)
other_funcs = copy.deepcopy(self.functions[fun].other_func)
s = self.functions[fun].__expr__
for i in range(len(other_funcs)):
other_funcs[i] = self.get_result(other_funcs[i], values)
s = re.sub("_TBR_", f"({str(other_funcs[i])})", s, count=1)
return eval(s, values)
a = {
"V": Formula("V", ["x", "y"], "G(y) * x / 3"),
"U": Formula("U", ["x", "y"], "G(y)**2 / (9 * V(x, y)) + V(x, y)"),
"G": Formula("G", ["y"], "1 - y")
}
p = Pyramid("Testing", a)
print(p.get_result("U", {"x":2,"y":3}))
我有一个对象列表,每个对象都是一个数学函数,这些函数可能相互依赖,例如:
[
Formula("G", ["y"], "1 - y"),
Formula("V", ["x", "y"], "G(y) * x / 3"),
Formula("U", ["x", "y"],"(G(y)**2) / (9 * V(x, y)) + V(x, y)")
]
其中第一个参数是函数名,第二个是使用的变量列表,第三个是字符串 - 函数的表达式。
有没有一种简单的方法来评估函数 U(x, y) 在给定点的值,例如在 [2, 3] 并递归调用 G(3)和V(2, 3),得到最后的结果?
我曾尝试在 Sympy 中执行此操作,但无法在函数 V(x,y)[ 中调用例如函数 G(y) =25=]
也许是这样的?
>>> def Formula(*args):
... return parse_expr('{%s(*%s): %s}' % args)
...
>>> f =[
... Formula("G", ["y"], "1 - y"),
... Formula("V", ["x", "y"], "G(y) * x / 3"),
... Formula("U", ["x", "y"],"(G(y)**2) / (9 * V(x, y)) + V(x, y)")
... ]
>>> f
[{G(y): 1 - y}, {V(x, y): x*G(y)/3}, {U(x, y): G(y)**2/(9*V(x, y)) + V(x, y)}]
f
已按拓扑排序,因此返回替换
>>> from sympy import Dict
>>> e=Dict(f[-1])
>>> e=e.subs(f[-2])
>>> e=e.subs(f[-3])
>>> a,b=dict(e).popitem()
>>> U = Lambda(a.args,b)
>>> U(2,3)
-5/3
如果它没有排序,你可以使用 topological_sort
,也许通过 repsort
here 这样做:
>>> repsort(*[tuple(i.items()) for i in f])
[(U(x, y), G(y)**2/(9*V(x, y)) + V(x, y)), (V(x, y), x*G(y)/3), (G(y), 1 - y)]
>>> s = _
>>> expr = s[0][0]
>>> for i in s:
... expr = expr.subs(*i)
...
>>> expr
x*(1 - y)/3 + (1 - y)/(3*x)
>>> U = Lambda(tuple(ordered(expr.free_symbols)), _)
>>> U(2,3)
-5/3
感谢您的所有建议。在我给出的示例中,Formula
是按拓扑顺序排列的,但实际上并不总是这样。
我可以使用@Stef 的解决方案,但我必须格式化公式表达式,然后 eval()
它
x,y = sympy.symbols('x y'); G = 1-y; V = G * x / 3; U = G**2 / (9*V+V)
然后@OscarBenjamin 建议使用 sympy 的 parse_expr
,效果很好,直到我意识到公式并不总是按拓扑顺序给出。所以我发现,尝试按拓扑顺序排列然后解析它会花费太多执行时间。
最终,我决定制作自己的解析器,它看起来像这样(测试 类 和变量):
import re
import copy
class Formula():
function_name = ""
variables = []
expression = ""
__expr__ = ""
other_func_calls = 0
def __init__(self, function_name:str, variables:list, fun:str) -> None:
self.function_name = function_name
self.variables = variables
self.expression = fun
other_func = []
for i in fun:
if ord(i) in range(ord("A"), ord("Z") + 1):
self.other_func_calls += 1
other_func.append(i)
self.__expr__ = re.sub('[A-Z]?\((\w|, )*\)','_TBR_', fun) # _TBR_ is being replaced later
self.other_func = other_func # list of other functions in chronological order
class Pyramid():
name:str
functions:dict[str:Formula]
def __init__(self, name:str, funs:dict[str:Formula]) -> None:
self.name = name
self.functions = funs
def get_result(self, fun:str, values:dict[str:int]):
if (self.functions[fun].other_func_calls == 0): # Function does not call other functions
return eval(self.functions[fun].expression, values)
other_funcs = copy.deepcopy(self.functions[fun].other_func)
s = self.functions[fun].__expr__
for i in range(len(other_funcs)):
other_funcs[i] = self.get_result(other_funcs[i], values)
s = re.sub("_TBR_", f"({str(other_funcs[i])})", s, count=1)
return eval(s, values)
a = {
"V": Formula("V", ["x", "y"], "G(y) * x / 3"),
"U": Formula("U", ["x", "y"], "G(y)**2 / (9 * V(x, y)) + V(x, y)"),
"G": Formula("G", ["y"], "1 - y")
}
p = Pyramid("Testing", a)
print(p.get_result("U", {"x":2,"y":3}))