def visit_Name(self, node): name = node.id newname = normalize((name, 0)) if name in self.variables: expr = Name(newname, Load()) return expr else: return node
def eval_formula(expr, dataframe=None, context=None): ''' expr: string Symbolic expression to evaluate. Example: `k(1)-delta*k-i` table: (optional) pandas dataframe Each column is a time series, which can be indexed with dolo notations. context: dict or CalibrationDict ''' 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(k)] = dd[k] from numpy import log, exp dd['log'] = log dd['exp'] = exp if dataframe is not None: import pandas as pd tvariables = dataframe.columns for k in tvariables: if k in dd: dd[k + '_ss'] = dd[k] # steady-state value dd[stringify((k, 0))] = dataframe[k] for h in range(1, 3): # maximum number of lags dd[stringify((k, -h))] = dataframe[k].shift(h) dd[stringify((k, h))] = dataframe[k].shift(-h) dd['t'] = pd.Series(dataframe.index, index=dataframe.index) import ast expr_ast = ast.parse(expr).body[0].value # nexpr = StandardizeDatesSimple(tvariables).visit(expr_ast) print(tvariables) nexpr = normalize(expr_ast, variables=tvariables) expr = to_source(nexpr) res = eval(expr, dd) return res
def parse_equation(eq_string, vars, substract_lhs=True, to_sympy=False): eq = eq_string.split('|')[0] # ignore complentarity constraints if '==' not in eq: eq = eq.replace('=', '==') expr = ast.parse(eq).body[0].value expr_std = normalize(expr, variables=vars) if isinstance(expr_std, Compare): lhs = expr_std.left rhs = expr_std.comparators[0] if substract_lhs: expr_std = BinOp(left=rhs, right=lhs, op=Sub()) else: if to_sympy: return [ast_to_sympy(lhs), ast_to_sympy(rhs)] return [lhs, rhs] if to_sympy: return ast_to_sympy(expr_std) else: return expr_std
def compile_higher_order_function(eqs, syms, params, order=2, funname='anonymous', return_code=False, compile=False): '''From a list of equations and variables, define a multivariate functions with higher order derivatives.''' from dolang import normalize, stringify vars = [s[0] for s in syms] # TEMP: compatibility fix when eqs is an Odict: eqs = [eq for eq in eqs] if isinstance(eqs[0], str): # elif not isinstance(eqs[0], sympy.Basic): # assume we have ASTs eqs = list([ast.parse(eq).body[0] for eq in eqs]) eqs_std = list([normalize(eq, variables=vars) for eq in eqs]) eqs_sym = list([ast_to_sympy(eq) for eq in eqs_std]) else: eqs_sym = eqs symsd = list([stringify((a, b)) for a, b in syms]) paramsd = list([stringify(a) for a in params]) D = higher_order_diff(eqs_sym, symsd, order=order) txt = """def {funname}(x, p, order=1): import numpy from numpy import log, exp, tan, sqrt from numpy import pi as pi_ from numpy import inf as inf_ from scipy.special import erfc """.format(funname=funname) for i in range(len(syms)): txt += " {} = x[{}]\n".format(symsd[i], i) txt += "\n" for i in range(len(params)): txt += " {} = p[{}]\n".format(paramsd[i], i) txt += "\n out = numpy.zeros({})".format(len(eqs)) for i in range(len(eqs)): txt += "\n out[{}] = {}".format(i, D[0][i]) txt += """ if order == 0: return out """ if order >= 1: # Jacobian txt += " out_1 = numpy.zeros(({},{}))\n".format(len(eqs), len(syms)) for i in range(len(eqs)): for j in range(len(syms)): val = D[1][i, j] if val != 0: txt += " out_1[{},{}] = {}\n".format(i, j, D[1][i, j]) txt += """ if order == 1: return [out, out_1] """ if order >= 2: # Hessian txt += " out_2 = numpy.zeros(({},{},{}))\n".format( len(eqs), len(syms), len(syms)) for n in range(len(eqs)): for i in range(len(syms)): for j in range(len(syms)): val = D[2][n, i, j] if val is not None: if val != 0: txt += " out_2[{},{},{}] = {}\n".format( n, i, j, D[2][n, i, j]) else: i1, j1 = sorted((i, j)) if D[2][n, i1, j1] != 0: txt += " out_2[{},{},{}] = out_2[{},{},{}]\n".format( n, i, j, n, i1, j1) txt += """ if order == 2: return [out, out_1, out_2] """ if order >= 3: # Hessian txt += " out_3 = numpy.zeros(({},{},{},{}))\n".format( len(eqs), len(syms), len(syms), len(syms)) for n in range(len(eqs)): for i in range(len(syms)): for j in range(len(syms)): for k in range(len(syms)): val = D[3][n, i, j, k] if val is not None: if val != 0: txt += " out_3[{},{},{},{}] = {}\n".format( n, i, j, k, D[3][n, i, j, k]) else: i1, j1, k1 = sorted((i, j, k)) if D[3][n, i1, j1, k1] != 0: txt += " out_3[{},{},{},{}] = out_3[{},{},{},{}]\n".format( n, i, j, k, n, i1, j1, k1) txt += """ if order == 3: return [out, out_1, out_2, out_3] """ if return_code: return txt else: d = {} d['division'] = division exec(txt, d) fun = d[funname] if compile: raise Exception("Not implemented.") return fun
def compile_higher_order_function(eqs, syms, params, order=2, funname='anonymous', return_code=False, compile=False): '''From a list of equations and variables, define a multivariate functions with higher order derivatives.''' from dolang import normalize, stringify vars = [s[0] for s in syms] # TEMP: compatibility fix when eqs is an Odict: eqs = [eq for eq in eqs] if isinstance(eqs[0], str): # elif not isinstance(eqs[0], sympy.Basic): # assume we have ASTs eqs = list([ast.parse(eq).body[0] for eq in eqs]) eqs_std = list( [normalize(eq, variables=vars) for eq in eqs] ) eqs_sym = list( [ast_to_sympy(eq) for eq in eqs_std] ) else: eqs_sym = eqs symsd = list( [stringify((a,b)) for a,b in syms] ) paramsd = list( [stringify(a) for a in params] ) D = higher_order_diff(eqs_sym, symsd, order=order) txt = """def {funname}(x, p, order=1): import numpy from numpy import log, exp, tan, sqrt from numpy import pi as pi_ from numpy import inf as inf_ from scipy.special import erfc """.format(funname=funname) for i in range(len(syms)): txt += " {} = x[{}]\n".format(symsd[i], i) txt += "\n" for i in range(len(params)): txt += " {} = p[{}]\n".format(paramsd[i], i) txt += "\n out = numpy.zeros({})".format(len(eqs)) for i in range(len(eqs)): txt += "\n out[{}] = {}".format(i, D[0][i]) txt += """ if order == 0: return out """ if order >= 1: # Jacobian txt += " out_1 = numpy.zeros(({},{}))\n".format(len(eqs), len(syms)) for i in range(len(eqs)): for j in range(len(syms)): val = D[1][i,j] if val != 0: txt += " out_1[{},{}] = {}\n".format(i,j,D[1][i,j]) txt += """ if order == 1: return [out, out_1] """ if order >= 2: # Hessian txt += " out_2 = numpy.zeros(({},{},{}))\n".format(len(eqs), len(syms), len(syms)) for n in range(len(eqs)): for i in range(len(syms)): for j in range(len(syms)): val = D[2][n,i,j] if val is not None: if val != 0: txt += " out_2[{},{},{}] = {}\n".format(n,i,j,D[2][n,i,j]) else: i1, j1 = sorted( (i,j) ) if D[2][n,i1,j1] != 0: txt += " out_2[{},{},{}] = out_2[{},{},{}]\n".format(n,i,j,n,i1,j1) txt += """ if order == 2: return [out, out_1, out_2] """ if order >= 3: # Hessian txt += " out_3 = numpy.zeros(({},{},{},{}))\n".format(len(eqs), len(syms), len(syms), len(syms)) for n in range(len(eqs)): for i in range(len(syms)): for j in range(len(syms)): for k in range(len(syms)): val = D[3][n,i,j,k] if val is not None: if val != 0: txt += " out_3[{},{},{},{}] = {}\n".format(n,i,j,k,D[3][n,i,j,k]) else: i1, j1, k1 = sorted( (i,j,k) ) if D[3][n,i1,j1,k1] != 0: txt += " out_3[{},{},{},{}] = out_3[{},{},{},{}]\n".format(n,i,j,k,n,i1,j1,k1) txt += """ if order == 3: return [out, out_1, out_2, out_3] """ print(txt) if return_code: return txt else: d = {} d['division'] = division exec(txt, d) fun = d[funname] if compile: raise Exception("Not implemented.") return fun