Пример #1
0
def timeshift(expr, variables, date):
    from sympy import Symbol
    from dolang import stringify
    d = {
        Symbol(stringify((v, 0))): Symbol(stringify((v, date)))
        for v in variables
    }
    return expr.subs(d)
Пример #2
0
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
Пример #3
0
    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
Пример #4
0
    def projection(self):  #, m: 'n_e', y: "n_y", p: "n_p"):

        if self.__projection__ is None:

            arguments_ = {
                # 'e': [(e,0) for e in self.model.symbols['exogenous']],
                # 's': [(e,0) for e in self.model.symbols['states']],
                # 'x': [(e,0) for e in self.model.symbols['controls']],
                'm': [(e, 0) for e in self.symbols['exogenous']],
                'y': [(e, 0) for e in self.symbols['aggregate']],
                'p': self.symbols['parameters']
            }

            vars = sum([[e[0] for e in h]
                        for h in [*arguments_.values()][:-1]], [])

            arguments = {
                k: [dolang.symbolic.stringify_symbol(e) for e in v]
                for k, v in arguments_.items()
            }

            preamble = {}  # for now

            projdefs = self.data.get('projection', {})
            pkeys = [*projdefs.keys()]
            n_p = len(pkeys)
            equations = [
                projdefs[v] for v in self.model.symbols['exogenous'][:n_p]
            ]
            equations = [
                dolang.stringify(eq, variables=vars) for eq in equations
            ]
            content = {f'{pkeys[i]}_0': eq for i, eq in enumerate(equations)}
            fff = FlatFunctionFactory(preamble, content, arguments,
                                      'equilibrium')
            fun = dolang.function_compiler.make_method_from_factory(
                fff, debug=self.debug)
            from dolang.vectorize import standard_function
            self.__projection__ = standard_function(fun[1], len(equations))

        return self.__projection__
Пример #5
0
    def ℰ(self):

        if self.__equilibrium__ is None:

            arguments_ = {
                'e': [(e, 0) for e in self.model.symbols['exogenous']],
                's': [(e, 0) for e in self.model.symbols['states']],
                'x': [(e, 0) for e in self.model.symbols['controls']],
                'm': [(e, 0) for e in self.symbols['exogenous']],
                'y': [(e, 0) for e in self.symbols['aggregate']],
                'p': self.symbols['parameters']
            }

            vars = sum([[e[0] for e in h]
                        for h in [*arguments_.values()][:-1]], [])

            arguments = {
                k: [dolang.symbolic.stringify_symbol(e) for e in v]
                for k, v in arguments_.items()
            }

            preamble = {}  # for now

            equations = [
                ("{}-({})".format(*(str(eq).split('='))) if '=' in eq else eq)
                for eq in self.data['equilibrium']
            ]
            equations = [
                dolang.stringify(eq, variables=vars) for eq in equations
            ]
            content = {f'eq_{i}': eq for i, eq in enumerate(equations)}
            fff = FlatFunctionFactory(preamble, content, arguments,
                                      'equilibrium')
            fun = dolang.function_compiler.make_method_from_factory(
                fff, debug=self.debug)
            from dolang.vectorize import standard_function
            self.__equilibrium__ = standard_function(fun[1], len(equations))

        return self.__equilibrium__
Пример #6
0
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']]

    if hasattr(model.symbolic, 'definitions'):
        definitions = model.symbolic.definitions
    else:
        definitions = {}

    d = dict()

    for k in definitions:
        v = parse_equation(definitions[k], all_variables, to_sympy=True)
        kk = stringify((k, 0))
        kk_m1 = stringify((k, -1))
        kk_1 = stringify((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.symbolic.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.symbolic.equations['transition']
    g_eqs = [
        parse_equation(eq, all_variables, to_sympy=True, substract_lhs=False)
        for eq in g_eqs
    ]
    #solve_recursively
    from collections import OrderedDict
    dd = OrderedDict()
    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['shocks']]

    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]
Пример #7
0
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
Пример #8
0
regex = re.compile("\{(.*)\}", re.MULTILINE)

m = regex.search(out.decode('utf8').replace("\n", ""))
txt = m.group(0)
data = json.loads(txt)

endo_vars = [e['name'] for e in data['modfile']['endogenous']]
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')
Пример #9
0
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