def eval_formula(expr: str, dataframe=None, context=None): ''' expr: string Symbolic expression to evaluate. Example: `k(1)-delta*k(0)-i` table: (optional) pandas dataframe Each column is a time series, which can be indexed with dolo notations. context: dict or CalibrationDict ''' print("Evaluating: {}".format(expr)) if context is None: dd = {} # context dictionary elif isinstance(context, CalibrationDict): dd = context.flat.copy() else: dd = context.copy() # compat since normalize form for parameters doesn't match calib dict. for k in [*dd.keys()]: dd[stringify_symbol(k)] = dd[k] expr_ast = parse_string(expr).value variables = list_variables(expr_ast) nexpr = stringify(expr_ast) print(expr) print(variables) dd['log'] = log dd['exp'] = exp if dataframe is not None: import pandas as pd for (k, t) in variables: dd[stringify_symbol((k, t))] = dataframe[k].shift(t) dd['t'] = pd.Series(dataframe.index, index=dataframe.index) expr = to_source(nexpr) print(expr) print(dd.keys()) res = eval(expr, dd) return res
def make_method(equations, arguments, constants, targets=None, rhs_only=False, definitions={}, funname='anonymous'): compat = lambda s: s.replace("^", "**").replace('==', '=').replace( '=', '==') equations = [compat(eq) for eq in equations] if isinstance(arguments, list): arguments = OrderedDict([('arg_{}'.format(i), k) for i, k in enumerate(arguments)]) ## replace = by == known_variables = [a[0] for a in sum(arguments.values(), [])] known_definitions = [a for a in definitions.keys()] known_constants = [a[0] for a in constants] all_variables = known_variables + known_definitions known_functions = [] known_constants = [] if targets is not None: all_variables.extend([o[0] for o in targets]) targets = [stringify(o) for o in targets] else: targets = ['_out_{}'.format(n) for n in range(len(equations))] all_symbols = all_variables # + known_constants equations = [parse(eq) for eq in equations] definitions = {k: parse(v) for k, v in definitions.items()} defs_incidence = {} for sym, val in definitions.items(): lvars = list_variables(val, vars=known_definitions) defs_incidence[(sym, 0)] = [v for v in lvars if v[0] in known_definitions] # return defs_incidence equations_incidence = {} to_be_defined = set([]) for i, eq in enumerate(equations): cn = CountNames(all_variables, known_functions, known_constants) cn.visit(eq) equations_incidence[i] = cn.variables to_be_defined = to_be_defined.union( [a for a in cn.variables if a[0] in known_definitions]) deps = [] for tv in to_be_defined: ndeps = get_deps(defs_incidence, tv) deps.extend(ndeps) deps = [d for d in unique(deps)] new_definitions = OrderedDict() for k in deps: val = definitions[k[0]] nval = time_shift(val, k[1], all_variables) new_definitions[stringify(k)] = normalize(nval, variables=all_symbols) new_equations = [] for n, eq in enumerate(equations): d = match(parse("_x == _y"), eq) if d is not False: lhs = d['_x'] rhs = d['_y'] if rhs_only: val = rhs else: val = ast.BinOp(left=rhs, op=Sub(), right=lhs) else: val = eq new_equations.append(normalize(val, variables=all_symbols)) # preambleIndex(Num(x)) preamble = [] for i, (arg_group_name, arg_group) in enumerate(arguments.items()): for pos, t in enumerate(arg_group): sym = stringify(t) rhs = Subscript(value=Name(id=arg_group_name, ctx=Load()), slice=Index(Num(pos)), ctx=Load()) val = Assign(targets=[Name(id=sym, ctx=Store())], value=rhs) preamble.append(val) for pos, p in enumerate(constants): sym = stringify(p) rhs = Subscript(value=Name(id='p', ctx=Load()), slice=Index(Num(pos)), ctx=Load()) val = Assign(targets=[Name(id=sym, ctx=Store())], value=rhs) preamble.append(val) # now construct the function per se body = [] for k, v in new_definitions.items(): line = Assign(targets=[Name(id=k, ctx=Store())], value=v) body.append(line) for n, neq in enumerate(new_equations): line = Assign(targets=[Name(id=targets[n], ctx=Store())], value=new_equations[n]) body.append(line) for n, neq in enumerate(new_equations): line = Assign(targets=[ Subscript(value=Name(id='out', ctx=Load()), slice=Index(Num(n)), ctx=Store()) ], value=Name(id=targets[n], ctx=Load())) body.append(line) from ast import arg, FunctionDef, Module from ast import arguments as ast_arguments f = FunctionDef( name=funname, args=ast_arguments(args=[arg(arg=a) for a in arguments.keys()] + [arg(arg='p'), arg(arg='out')], vararg=None, kwarg=None, kwonlyargs=[], kw_defaults=[], defaults=[]), body=preamble + body, decorator_list=[]) mod = Module(body=[f]) mod = ast.fix_missing_locations(mod) return mod
def test_list_variables(): from dolang.symbolic import parse_string e = parse_string('sin(a(1)+b+f(1)+f(+4)+sin(a)+k*cos(a(0)))') list_variables(e) assert (list_variables(e) == [('a', 1), ('f', 1), ('f', 4), ('a', 0)]) assert (list_variables(e, funs=['f']) == [('a', 1), ('a', 0)])
def test_list_variables(): from dolang.symbolic import parse_string e = parse_string("sin(a(1)+b+f(1)+f(4)+sin(a)+k*cos(a(0)))") list_variables(e) assert list_variables(e) == [("a", 1), ("f", 1), ("f", 4), ("a", 0)]