def equations(self): vars = self.variables + [*self.definitions.keys()] d = dict() for g, v in self.data['equations'].items(): ll = [] for eq in v: if "|" in eq: eq = eq.split("|")[0] ll.append(sanitize(eq, variables=vars)) d[g] = ll if "controls_lb" not in d: for ind, g in enumerate(("controls_lb", "controls_ub")): eqs = [] for i, eq in enumerate(self.data['equations']['arbitrage']): if "|" not in eq: if ind == 0: eq = "-inf" else: eq = "inf" else: comp = eq.split("|")[1] v = self.symbols["controls"][i] eq = decode_complementarity(comp, v)[ind] eqs.append(eq) d[g] = eqs return d
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 ℰ(self): if self.__equilibrium__ is None: if self.features["with-aggregate-states"]: 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"]], "S": [(e, 0) for e in self.symbols["states"]], "X": [(e, 0) for e in self.symbols["aggregate"]], "m_1": [(e, 1) for e in self.symbols["exogenous"]], "S_1": [(e, 1) for e in self.symbols["states"]], "X_1": [(e, 1) for e in self.symbols["aggregate"]], "p": self.symbols["parameters"], } else: 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"]], "X": [(e, 0) for e in self.symbols["aggregate"]], "m_1": [(e, 1) for e in self.symbols["exogenous"]], "X_1": [(e, 1) 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 from dolang.symbolic import sanitize, stringify eqs = parse_string(self.data["equilibrium"], start="equation_block") eqs = sanitize(eqs, variables=vars) eqs = stringify(eqs) content = {} for i, eq in enumerate(eqs.children): lhs, rhs = eq.children content[f"eq_{i}"] = "({1})-({0})".format( str_expression(lhs), str_expression(rhs)) fff = FlatFunctionFactory(preamble, content, arguments, "equilibrium") _, gufun = dolang.function_compiler.make_method_from_factory( fff, debug=self.debug) from dolang.vectorize import standard_function self.__equilibrium__ = standard_function(gufun, len(content)) return self.__equilibrium__
def test_sanitize(): from dolang.symbolic import sanitize, parse_string from dolang.codegen import to_source s = 'sin(a(1)+b+a+f(-1)+f(+4)+a(1))' expected = "sin(a(1) + b + a(0) + f(-(1)) + f(4) + a(1))" e = parse_string('sin(a(1)+b+a+f(-1)+f(+4)+a(1))') enes = sanitize(e, variables=['a', 'f']) assert (to_source(enes) == expected) # it also works with the string directly assert (sanitize(s, variables=['a', 'f']) == expected) # we also deal with = signs, and convert to python exponents assert (sanitize("a(1) = a^3 + b") == "a(1) == (a) ** (3) + b")
def projection(self): # , m: 'n_e', y: "n_y", p: "n_p"): # TODO: # behaves in a very misleading way if wrong number of argument is supplied # if no aggregate states, projection(m,x) (instead of projection(m,x,p)) returns zeros if self.__projection__ is None: if self.features["with-aggregate-states"]: arguments_ = { "m": [(e, 0) for e in self.symbols["exogenous"]], "S": [(e, 0) for e in self.symbols["states"]], "X": [(e, 0) for e in self.symbols["aggregate"]], "p": self.symbols["parameters"], } else: arguments_ = { "m": [(e, 0) for e in self.symbols["exogenous"]], "X": [(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 from dolang.symbolic import sanitize, stringify eqs = parse_string(self.data["projection"], start="assignment_block") eqs = sanitize(eqs, variables=vars) eqs = stringify(eqs) content = {} for eq in eqs.children: lhs, rhs = eq.children content[str_expression(lhs)] = str_expression(rhs) fff = FlatFunctionFactory(preamble, content, arguments, "equilibrium") _, gufun = dolang.function_compiler.make_method_from_factory( fff, debug=self.debug) from dolang.vectorize import standard_function self.__projection__ = standard_function(gufun, len(content)) return self.__projection__
def 𝒢(self): if (self.__transition__ is None) and self.features["with-aggregate-states"]: arguments_ = { "m_m1": [(e, -1) for e in self.symbols["exogenous"]], "S_m1": [(e, -1) for e in self.symbols["states"]], "X_m1": [(e, -1) for e in self.symbols["aggregate"]], "m": [(e, 0) for e in self.symbols["exogenous"]], "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 from dolang.symbolic import ( sanitize, parse_string, str_expression, stringify, ) eqs = parse_string(self.data["transition"], start="assignment_block") eqs = sanitize(eqs, variables=vars) eqs = stringify(eqs) content = {} for i, eq in enumerate(eqs.children): lhs, rhs = eq.children content[str_expression(lhs)] = str_expression(rhs) from dolang.factory import FlatFunctionFactory fff = FlatFunctionFactory(preamble, content, arguments, "transition") _, gufun = dolang.function_compiler.make_method_from_factory( fff, debug=self.debug) from dolang.vectorize import standard_function self.__transition__ = standard_function(gufun, len(content)) return self.__transition__
def equations(self): import yaml.nodes if self.__equations__ is None: vars = self.variables + [*self.definitions.keys()] d = dict() for g, v in self.data["equations"].items(): # new style if isinstance(v, yaml.nodes.ScalarNode): assert v.style == "|" if g in ("arbitrage", ): start = "complementarity_block" else: start = "assignment_block" eqs = parse_string(v, start=start) eqs = sanitize(eqs, variables=vars) eq_list = eqs.children # old style else: eq_list = [] for eq_string in v: start = "equation" # it should be assignment eq = parse_string(eq_string, start=start) eq = sanitize(eq, variables=vars) eq_list.append(eq) if g in ("arbitrage", ): ll = [] # List[str] ll_lb = [] # List[str] ll_ub = [] # List[str] with_complementarity = False for i, eq in enumerate(eq_list): if eq.data == "double_complementarity": v = eq.children[1].children[1].children[ 0].children[0].value t = int(eq.children[1].children[1].children[1]. children[0].value) expected = ( self.symbols["controls"][i], 0, ) # TODO raise nice error message if (v, t) != expected: raise Exception( f"Incorrect variable in complementarity: expected {expected}. Found {(v,t)}" ) ll_lb.append( str_expression(eq.children[1].children[0])) ll_ub.append( str_expression(eq.children[1].children[2])) eq = eq.children[0] with_complementarity = True else: ll_lb.append("-inf") ll_ub.append("inf") from dolang.symbolic import list_symbols # syms = list_symbols(eq) ll.append(str_expression(eq)) d[g] = ll if with_complementarity: d[g + "_lb"] = ll_lb d[g + "_ub"] = ll_ub else: # TODO: we should check here that equations are well specified d[g] = [str_expression(e) for e in eq_list] # if "controls_lb" not in d: # for ind, g in enumerate(("controls_lb", "controls_ub")): # eqs = [] # for i, eq in enumerate(d['arbitrage']): # if "⟂" not in eq: # if ind == 0: # eq = "-inf" # else: # eq = "inf" # else: # comp = eq.split("⟂")[1].strip() # v = self.symbols["controls"][i] # eq = decode_complementarity(comp, v+"[t]")[ind] # eqs.append(eq) # d[g] = eqs self.__equations__ = d return self.__equations__
def definitions(self): from yaml import ScalarNode if self.__definitions__ is None: # at this stage, basic_symbols doesn't contain auxiliaries basic_symbols = self.symbols vars = sum( [ basic_symbols[k] for k in basic_symbols.keys() if k != "parameters" ], [], ) # # auxiliaries = [remove_timing(parse_string(k)) for k in self.data.get('definitions', {})] # # auxiliaries = [str_expression(e) for e in auxiliaries] # # symbols['auxiliaries'] = auxiliaries if "definitions" not in self.data: self.__definitions__ = {} # self.__symbols__['auxiliaries'] = [] elif isinstance(self.data["definitions"], ScalarNode): definitions = {} # new-style from lark import Token def_block_tree = parse_string(self.data["definitions"], start="assignment_block") def_block_tree = sanitize( def_block_tree ) # just to replace (v,) by (v,0) # TODO: remove auxiliaries = [] for eq_tree in def_block_tree.children: lhs, rhs = eq_tree.children tok_name: Token = lhs.children[0].children[0] tok_date: Token = lhs.children[1].children[0] name = tok_name.value date = int(tok_date.value) if name in vars: raise Exception( f"definitions:{tok_name.line}:{tok_name.column}: Auxiliary variable '{name}'' already defined." ) if date != 0: raise Exception( f"definitions:{tok_name.line}:{tok_name.column}: Auxiliary variable '{name}' must be defined at date 't'." ) # here we could check some stuff from dolang import list_symbols syms = list_symbols(rhs) for p in syms.parameters: if p in vars: raise Exception( f"definitions:{tok_name.line}: Symbol '{p}' is defined as a variable. Can't appear as a parameter." ) if p not in self.symbols["parameters"]: raise Exception( f"definitions:{tok_name.line}: Paremeter '{p}' must be defined as a model symbol." ) for v in syms.variables: if v[0] not in vars: raise Exception( f"definitions:{tok_name.line}: Variable '{v[0]}[t]' is not defined." ) auxiliaries.append(name) vars.append(name) definitions[str_expression(lhs)] = str_expression(rhs) self.__symbols__["auxiliaries"] = auxiliaries self.__definitions__ = definitions else: # old style from dolang.symbolic import remove_timing auxiliaries = [ remove_timing(parse_string(k)) for k in self.data.get("definitions", {}) ] auxiliaries = [str_expression(e) for e in auxiliaries] self.__symbols__["auxiliaries"] = auxiliaries vars = self.variables auxs = [] definitions = self.data["definitions"] d = dict() for i in range(len(definitions.value)): kk = definitions.value[i][0] if self.__compat__: k = parse_string(kk.value) if k.data == "symbol": # TODO: warn that definitions should be timed from dolang.grammar import create_variable k = create_variable(k.children[0].value, 0) else: k = parse_string(kk.value, start="variable") k = sanitize(k, variables=vars) assert k.children[1].children[0].value == "0" vv = definitions.value[i][1] v = parse_string(vv, start="formula") v = sanitize(v, variables=vars) v = str_expression(v) key = str_expression(k) vars.append(key) d[key] = v auxs.append(remove_timing(key)) self.__symbols__["auxiliaries"] = auxs self.__definitions__ = d return self.__definitions__