def __init__(self, values, model=None): from dolang.symbolic import sanitize, stringify exogenous = model.symbols['exogenous'] states = model.symbols['states'] controls = model.symbols['controls'] parameters = model.symbols['parameters'] preamble = dict([(s, values[s]) for s in values.keys() if s not in controls]) equations = [values[s] for s in controls] variables = exogenous + states + controls + [*preamble.keys()] preamble_str = dict() for k in [*preamble.keys()]: v = preamble[k] if '(' not in k: vv = f'{k}(0)' else: vv = k preamble_str[stringify(vv)] = stringify(sanitize(v, variables)) # let's reorder the preamble from dolang.triangular_solver import get_incidence, triangular_solver incidence = get_incidence(preamble_str) sol = triangular_solver(incidence) kk = [*preamble_str.keys()] preamble_str = dict([(kk[k], preamble_str[kk[k]]) for k in sol]) equations = [ dolang.symbolic.sanitize(eq, variables) for eq in equations ] equations_strings = [ dolang.stringify(eq, variables) for eq in equations ] args = dict([('m', [(e, 0) for e in exogenous]), ('s', [(e, 0) for e in states]), ('p', [e for e in parameters])]) args = dict([(k, [stringify_symbol(e) for e in v]) for k, v in args.items()]) targets = [stringify_symbol((e, 0)) for e in controls] eqs = dict([(targets[i], eq) for i, eq in enumerate(equations_strings)]) fff = FlatFunctionFactory(preamble_str, eqs, args, 'custom_dr') fun, gufun = make_method_from_factory(fff) self.p = model.calibration['parameters'] self.exo_grid = model.exogenous.discretize() # this is never used self.endo_grid = model.get_grid() self.gufun = gufun
def timeshift(expr, variables, date): from sympy import Symbol from dolang import stringify d = { Symbol(stringify_symbol((v, 0))): Symbol(stringify_symbol((v, date))) for v in variables } return expr.subs(d)
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 get_factory(model, eq_type: str, tshift: int = 0): from dolo.compiler.model import decode_complementarity from dolo.compiler.recipes import recipes from dolang.symbolic import stringify, stringify_symbol equations = model.equations if eq_type == "auxiliary": eqs = [('{}({})'.format(s, 0)) for s in model.symbols['auxiliaries']] specs = { 'eqs': [['exogenous', 0, 'm'], ['states', 0, 's'], ['controls', 0, 'x'], ['parameters', 0, 'p']] } else: eqs = equations[eq_type] if eq_type in ('controls_lb', 'controls_ub'): specs = { 'eqs': recipes['dtcc']['specs']['arbitrage']['complementarities'][ 'left-right'] } else: specs = recipes['dtcc']['specs'][eq_type] specs = shift_spec(specs, tshift=tshift) preamble_tshift = set([s[1] for s in specs['eqs'] if s[0] == 'states']) preamble_tshift = preamble_tshift.intersection( set([s[1] for s in specs['eqs'] if s[0] == 'controls'])) args = [] for sg in specs['eqs']: if sg[0] == 'parameters': args.append([s for s in model.symbols["parameters"]]) else: args.append([(s, sg[1]) for s in model.symbols[sg[0]]]) args = [[stringify_symbol(e) for e in vg] for vg in args] arguments = dict(zip([sg[2] for sg in specs['eqs']], args)) # temp eqs = [eq.replace("==","=").replace("=","==") for eq in eqs] if 'target' in specs: sg = specs['target'] targets = [(s, sg[1]) for s in model.symbols[sg[0]]] eqs = [eq.split('==')[1] for eq in eqs] else: eqs = [("({1})-({0})".format(*eq.split('==')) if '==' in eq else eq) for eq in eqs] targets = [('out{}'.format(i), 0) for i in range(len(eqs))] eqs = [str.strip(eq) for eq in eqs] eqs = [dolang.parse_string(eq) for eq in eqs] es = ExpressionSanitizer(model.variables) eqs = [es.visit(eq) for eq in eqs] eqs = [time_shift(eq, tshift) for eq in eqs] eqs = [stringify(eq) for eq in eqs] eqs = [dolang.to_source(eq) for eq in eqs] targets = [stringify_symbol(e) for e in targets] # sanitize defs ( should be ) defs = dict() for k in model.definitions: if '(' not in k: s = "{}(0)".format(k) val = model.definitions[k] val = es.visit(dolang.parse_string(val)) for t in preamble_tshift: s = stringify_symbol((k, t)) vv = stringify(time_shift(val, t)) defs[s] = dolang.to_source(vv) preamble = reorder_preamble(defs) eqs = dict(zip(targets, eqs)) ff = FlatFunctionFactory(preamble, eqs, arguments, eq_type) return ff
def model_to_fg(model, order=2): # compile f, g function at higher order all_variables = sum( [model.symbols[e] for e in model.symbols if e != "parameters"], []) all_dvariables = ([(d, 0) for d in all_variables] + [(d, 1) for d in all_variables] + [(d, -1) for d in all_variables]) psyms = [(e, 0) for e in model.symbols["parameters"]] definitions = model.definitions d = dict() for k in definitions: v = parse_equation(definitions[k], all_variables, to_sympy=True) kk = stringify_symbol((k, 0)) kk_m1 = stringify_symbol((k, -1)) kk_1 = stringify_symbol((k, 1)) d[sympy.Symbol(kk)] = v d[sympy.Symbol(kk_m1)] = timeshift(v, all_variables, -1) d[sympy.Symbol(kk_1)] = timeshift(v, all_variables, 1) f_eqs = model.equations["arbitrage"] f_eqs = [parse_equation(eq, all_variables, to_sympy=True) for eq in f_eqs] f_eqs = [eq.subs(d) for eq in f_eqs] g_eqs = model.equations["transition"] g_eqs = [ parse_equation(eq, all_variables, to_sympy=True, substract_lhs=False) for eq in g_eqs ] # solve_recursively dd = dict() for eq in g_eqs: dd[eq[0]] = eq[1].subs(dd).subs(d) g_eqs = dd.values() f_syms = ([(e, 0) for e in model.symbols["states"]] + [(e, 0) for e in model.symbols["controls"]] + [(e, 1) for e in model.symbols["states"]] + [(e, 1) for e in model.symbols["controls"]]) g_syms = ([(e, -1) for e in model.symbols["states"]] + [(e, -1) for e in model.symbols["controls"]] + [(e, 0) for e in model.symbols["exogenous"]]) params = model.symbols["parameters"] f = compile_higher_order_function( f_eqs, f_syms, params, order=order, funname="f", return_code=False, compile=False, ) g = compile_higher_order_function( g_eqs, g_syms, params, order=order, funname="g", return_code=False, compile=False, ) # cache result model.__higher_order_functions__ = dict(f=f, g=g) model.__highest_order__ = order return [f, g]
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.symbolic import stringify, stringify_symbol 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([stringify_symbol(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_symbol((a, b)) for a, b in syms]) paramsd = list([stringify_symbol(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 = {} exec(txt, d) fun = d[funname] if compile: raise Exception("Not implemented.") return fun
exo_vars = [e['name'] for e in data['modfile']['exogenous']] parameters = [e['name'] for e in data['modfile']['parameters']] variables = endo_vars + exo_vars # all variables with time-index equations = [f"{e['rhs']} - ({e['lhs']})" for e in data['modfile']['model']] equations = [dolang.symbolic.sanitize(eq, variables) for eq in equations] equations_strings = [dolang.stringify(eq, variables) for eq in equations] args = dict([('y_f', [(e, 1) for e in endo_vars]), ('y', [(e, 0) for e in endo_vars]), ('y_p', [(e, -1) for e in endo_vars]), ('e', [(e, 0) for e in exo_vars]), ('p', [e for e in parameters])]) args = dict([(k, [stringify_symbol(e) for e in v]) for k, v in args.items()]) from dolang.symbolic import stringify_symbol from dolang.factory import FlatFunctionFactory eqs = dict([(f"equation_{i+1}", eq) for i, eq in enumerate(equations_strings)]) fff = FlatFunctionFactory(dict(), eqs, args, 'f_dynamic') from dolang.function_compiler import make_method_from_factory fun, gufun = make_method_from_factory(fff, vectorize=True, debug=True) calibration = dict() data['modfile']['statements']
def model_to_fg(model, order=2): # compile f, g function at higher order all_variables = sum( [model.symbols[e] for e in model.symbols if e != 'parameters'], []) all_dvariables = ([(d, 0) for d in all_variables] + [ (d, 1) for d in all_variables ] + [(d, -1) for d in all_variables]) psyms = [(e, 0) for e in model.symbols['parameters']] definitions = model.definitions d = dict() for k in definitions: v = parse_equation(definitions[k], all_variables, to_sympy=True) kk = stringify_symbol((k, 0)) kk_m1 = stringify_symbol((k, -1)) kk_1 = stringify_symbol((k, 1)) d[sympy.Symbol(kk)] = v d[sympy.Symbol(kk_m1)] = timeshift(v, all_variables, -1) d[sympy.Symbol(kk_1)] = timeshift(v, all_variables, 1) f_eqs = model.equations['arbitrage'] f_eqs = [parse_equation(eq, all_variables, to_sympy=True) for eq in f_eqs] f_eqs = [eq.subs(d) for eq in f_eqs] g_eqs = model.equations['transition'] g_eqs = [ parse_equation(eq, all_variables, to_sympy=True, substract_lhs=False) for eq in g_eqs ] #solve_recursively dd = dict() for eq in g_eqs: dd[eq[0]] = eq[1].subs(dd).subs(d) g_eqs = dd.values() f_syms = [(e,0) for e in model.symbols['states']] + \ [(e,0) for e in model.symbols['controls']] + \ [(e,1) for e in model.symbols['states']] + \ [(e,1) for e in model.symbols['controls']] g_syms = [(e,-1) for e in model.symbols['states']] + \ [(e,-1) for e in model.symbols['controls']] + \ [(e,0) for e in model.symbols['exogenous']] params = model.symbols['parameters'] f = compile_higher_order_function( f_eqs, f_syms, params, order=order, funname='f', return_code=False, compile=False) g = compile_higher_order_function( g_eqs, g_syms, params, order=order, funname='g', return_code=False, compile=False) # cache result model.__higher_order_functions__ = dict(f=f, g=g) model.__highest_order__ = order return [f, g]
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.symbolic import stringify, stringify_symbol 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( [stringify_symbol(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_symbol((a,b)) for a,b in syms] ) paramsd = list( [stringify_symbol(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 = {} exec(txt, d) fun = d[funname] if compile: raise Exception("Not implemented.") return fun
def get_factory(model, eq_type: str, tshift: int = 0): from dolo.compiler.model import decode_complementarity from dolo.compiler.recipes import recipes from dolang.symbolic import stringify, stringify_symbol equations = model.equations if eq_type == "auxiliary": eqs = ["{}".format(s) for s in model.symbols["auxiliaries"]] specs = { "eqs": [ ["exogenous", 0, "m"], ["states", 0, "s"], ["controls", 0, "x"], ["parameters", 0, "p"], ] } else: eqs = equations[eq_type] if eq_type in ("arbitrage_lb", "arbitrage_ub"): specs = { "eqs": recipes["dtcc"]["specs"]["arbitrage"]["complementarities"] ["left-right"] } else: specs = recipes["dtcc"]["specs"][eq_type] specs = shift_spec(specs, tshift=tshift) preamble_tshift = set([s[1] for s in specs["eqs"] if s[0] == "states"]) preamble_tshift = preamble_tshift.intersection( set([s[1] for s in specs["eqs"] if s[0] == "controls"])) args = [] for sg in specs["eqs"]: if sg[0] == "parameters": args.append([s for s in model.symbols["parameters"]]) else: args.append([(s, sg[1]) for s in model.symbols[sg[0]]]) args = [[stringify_symbol(e) for e in vg] for vg in args] arguments = dict(zip([sg[2] for sg in specs["eqs"]], args)) # temp eqs = [eq.split("⟂")[0].strip() for eq in eqs] if "target" in specs: sg = specs["target"] targets = [(s, sg[1]) for s in model.symbols[sg[0]]] eqs = [eq.split("=")[1] for eq in eqs] else: eqs = [("({1})-({0})".format(*eq.split("=")) if "=" in eq else eq) for eq in eqs] targets = [("out{}".format(i), 0) for i in range(len(eqs))] eqs = [str.strip(eq) for eq in eqs] eqs = [dolang.parse_string(eq) for eq in eqs] es = Sanitizer(variables=model.variables) eqs = [es.transform(eq) for eq in eqs] eqs = [time_shift(eq, tshift) for eq in eqs] eqs = [stringify(eq) for eq in eqs] eqs = [str_expression(eq) for eq in eqs] targets = [stringify_symbol(e) for e in targets] # sanitize defs ( should be ) defs = dict() for k in model.definitions: val = model.definitions[k] # val = es.transform(dolang.parse_string(val)) for t in preamble_tshift: s = stringify(time_shift(k, t)) if isinstance(val, str): vv = stringify(time_shift(val, t)) else: vv = str(val) defs[s] = vv preamble = reorder_preamble(defs) eqs = dict(zip(targets, eqs)) ff = FlatFunctionFactory(preamble, eqs, arguments, eq_type) return ff