daredare.model.model

Package daredare :: Package model :: Module model |
[hide private]
[frames] | no frames]

# Source Code for Module daredare.model.model

      1  #from xml.etree import ElementTree as ET
      2  import copy
      3  from sympy import __version__ as sympy_version
      4  print("Sympy version is : " + sympy_version)
      5  from sympy import *
      6  #from sympy.printing.latex import LatexPrinter
      7
      8  import misc.calculus
      9  import re
     10
     11 -class Variable(Symbol):
     12      '''
     13      Define a new variable with : x = Variable('x')
     14      Then x(k) returns the value with lag k
     15      Internally x(-k) is a symbol with name x_bk
     16      '''
     17      # question : should this class be a singleton ?
     18
     19      # other question : should variables be injected ? (import inspect ...)
     20
     21      basename = None
     22      name = None #Name of the variable
     23      init_value = None
     24      end_value = None
     25
     26 -    def __init__(self, name):
     27
     28          super(Variable,self).__init__()
     29          self.basename = name
     30          self.lag = 0
     31
     32          return(None)
     33
     34 -    def __call__(self,lag):
     35          newlag = self.lag + lag
     36          if newlag < 0:
     37              newname = self.basename + "_b" + str(- newlag)
     38              #newname = self.basename + "(" + str( newlag) + ")"
     39          elif newlag > 0:
     40              newname = self.basename + "_f" + str( newlag )
     41              #newname = self.basename + "(" + str( newlag ) + ")"
     42          else:
     43              newname = self.basename
     44          v = Variable(newname)
     45          v.lag = newlag
     46          v.basename = self.basename
     47          return v
     48
     49 -    def tostr(self, level=0):
     50          precedence = self.precedence
     51          if self.lag != 0:
     52              result = self.basename + "(" + str(self.lag) + ")"
     53          else:
     54              result = self.basename
     55          if precedence<=level:
     56              return('(%s)' % (result))
     57          return result
     58
     59 -class Parameter(Symbol):
     60
     61      name = None
     62      value = None
     63
     64 -    def __init__(self, name, value):
     65          super(Parameter,self).__init__()
     66          self.name = name
     67          self.value = value
     68          # Should this be kept her or not ?
     69          # If not is it useful to subclass Symbol ?
     70          return(None)
     71
     72 -    def __repr__(self):
     73          return(self.name)
     74
     75 -class Equation(Equality):
     76
     77      name = None
     78
     79 -    def __init__(self, lhs, rhs, name=None,is_endogenous=True):
     80          super(Equality,self).__init__()
     81          self.name = name
     82          self.is_endogenous=is_endogenous
     83          return(None)
     84
     85 -    def gap(self):
     86          return( -self.lhs + self.rhs)
     87
     88 -class Model:
     89
     90      fname = None # Model basename
     91      variables = []
     92      exovariables = []
     93      shocks = []
     94      covariances = []
     95      parameters = []
     96      equations = []
     97      init_values = dict()
     98      variables_order = dict()
     99      commands = None # keep some instructions to treat the model, not sure how to do it
    100
    101 -    def __init__(self, fname):
    102          self.fname = fname
    103          return(None)
    104
    105 -    def copy(self):
    106          c = Model(self.fname)
    107          c.variables = copy.copy(self.variables)
    108          c.exovariables = copy.copy(self.exovariables)
    109          c.covariances = copy.copy(self.covariances)
    110          c.parameters = copy.copy(self.parameters)
    111          c.equations = copy.copy(self.equations)
    112          c.init_values = copy.copy(self.init_values)
    113          c.commands = copy.copy(self.commands)
    114          return(c)
    115
    116 -    def check_all(self):
    117          print("There are :")
    118          print("- %s variables") % len(self.variables)
    119          print("- %s exovariables") % len(self.exovariables)
    120          print("- %s shocks") % len(self.shocks)
    121          print("- %s equations") % len(self.equations)
    122
    123 -    def map_function_to_expression(self,f,expr):
    124          if len(expr._args)==0 :
    125              return( f(expr) )
    126          else:
    127              l = list(expr._args)
    128              args = []
    129              for a in l:
    130                  args.append(self.map_function_to_expression(f,a))
    131              return( expr.__class__(* args) )
    132
    133 -    def current_equations(self):
    134          c_eqs = []
    135          def f(x):
    136              if x in self.shocks:
    137                  return(0)
    138              elif x.__class__ == Variable:
    139                  return(x(-x.lag))
    140              else:
    141                  return(x)
    142          for eq in self.equations:
    143              n_eq = self.map_function_to_expression(f,eq)
    144              n_eq.is_endogenous = eq.is_endogenous
    145              c_eqs.append(n_eq)
    146          return(c_eqs)
    147
    148 -    def future_variables(self):
    149          # returns [f_vars, f_eq, f_eq_n]
    150          # f_vars : list of variables with lag > 1
    151          # f_eq : list of equations containing future variables
    152          # f_eq_n : list of indices of equations containing future variables
    153
    154          f_eq_n = [] # indices of future equations
    155          f_eq = [] # future equations (same order)
    156          f_vars = set([]) # future variables
    157          for i in range(len(self.equations)):
    158              eq = self.equations[i]
    159              all_atoms = eq.atoms()
    160              f_var = []
    161              for a in all_atoms:
    162                  if (a.__class__ == Variable) and (a(-a.lag) in self.variables):
    163                      if a.lag > 0:
    164                          f_var.append(a)
    165              if len(f_var)>0:
    166                  f_eq_n.append(i)
    167                  f_eq.append(eq)
    168                  f_vars = f_vars.union(f_var)
    169          f_vars = list(f_vars)
    170          return([f_vars,f_eq,f_eq_n])
    171
    172 -    def get_jacobian(self):
    173          '''
    174          returns the jacobian of the equations with respect to
    175          all_variables_f,all_variables,all_variables_b
    176          where all_variables == variables + exovariables
    177          '''
    178          #print(self.variables)
    179          all_variables = self.variables + self.exovariables
    180          vec = map(lambda v: v(1), all_variables) + all_variables + map(lambda v: v(-1), all_variables)
    181          vec = Matrix([vec])
    182          #print vec
    183          f = Matrix( map(lambda eq: eq.gap(),self.equations) )
    184          return f.T.jacobian(vec)
    185
    186 -    def process_portfolio_model(self):
    187
    188          pf_model = self.copy()    # I should work on a copy on the model not on itself !
    189          pf_model.fname = pf_model.fname + "_pf"
    190
    191          [f_vars,f_eq,f_eq_n] = pf_model.future_variables()
    192
    193          f_vars_c = map(lambda v: v(-v.lag), f_vars) # this returns current dates of future variables
    194
    195          nf_eq_n = list(set(range(len(self.equations))).difference(f_eq_n)) # indices of non forward equations
    196          nf_eq = map(lambda (n): self.equations[n], nf_eq_n) # non forward equations (differences)
    197
    198          nf_eq = map(lambda eq: eq.gap(), nf_eq) # we remove equal signs before we can calculate jacobian
    199          f_eq = map(lambda eq: eq.gap(), f_eq)
    200
    201          A = Matrix(len(nf_eq),len(self.exovariables), lambda i,j : Variable("A_"+str(i+1)+str(j+1)))
    202          for a in A:
    203              pf_model.variables.append(a)
    204
    205          # Thanks to non forward equations, forward variables variations are
    206          # approximated by A * shocks (first order approximation)
    207          f = Matrix(nf_eq)
    208          f_X = f.jacobian(f_vars_c)
    209          for v in pf_model.exovariables:
    210              f_X = f_X.subs(v,0)
    211          f_E = f.jacobian(Matrix(self.exovariables))
    212          for v in pf_model.exovariables:
    213              f_E = f_E.subs(v,0)
    214          f_ = f_X * A + f_E
    215
    216          # Then future variables are substituted in forward equations
    217          # which are taylorized twice with respect to shocks
    218          # Cross products of shocks are replaced by covariances
    219          s_f_eq = [] # will contain future equations with substitutions
    220          for i in range(len(f_eq)):
    221              eq = f_eq[i]
    222              for k in range(len(f_vars)):
    223                  v = f_vars[k]
    224                  dv = (A[k,:] * Matrix(self.exovariables))[0,0]
    225                  eq = eq.subs(v,v+dv)
    226              seq = Calculus.second_order_taylorisation(eq, self.exovariables, self.covariances)
    227              s_f_eq.append(Equality(0,seq))
    228
    229          for eq in f_:
    230              pf_model.equations.append(Equation(0,eq,"Another moment condition") )
    231
    232          for i in range(len(f_eq)):
    233              pf_model.equations[f_eq_n[i]] = s_f_eq[i]
    234
    235          return(pf_model)
    236