I have a list of objects, each object is a mathematical function, and those functions may be dependent on each other, for example:
[
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)")
]
Where first argument is function name, second one is list of used variables, and third one is string - the function's expression.
Is there a simple way to evaluate value of function U(x, y) at a given point, for example, at [2, 3] and recursively call G(3) and V(2, 3), and get the final result?
I have tried to do this in Sympy, but couldn't call for example function G(y) in function V(x,y)
CodePudding user response:
Maybe something like this?
>>> 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
is already topologically sorted so back substitute
>>> 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
CodePudding user response:
Thanks for all your suggestions. In example I have given, the Formula
s are in topological order, but in practice it will not always be so.
I could use @Stef's solution, but I would have to format the formula expressions and then eval()
it
x,y = sympy.symbols('x y'); G = 1-y; V = G * x / 3; U = G**2 / (9*V V)
Then @OscarBenjamin suggested to use sympy's parse_expr
which worked just fine, until I realized that formulas will not always be given in topological order. So I found out, that trying to put it in topological order and then parse it, would take too much execution time.
Eventually, I decided to make my own parser, which looks something like this (test classes and variables):
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}))