PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/pymaclab/dsge/macrolab.py

https://github.com/TomAugspurger/pymaclab
Python | 2621 lines | 2525 code | 19 blank | 77 comment | 53 complexity | 2637e569eaff552230e415e7c1218d1d MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. '''
  2. .. module:: macrolab
  3. :platform: Linux
  4. :synopsis: The macrolab module contains the the most important classes for doing work with DSGE models. In particular it supplies
  5. the DSGEmodel class containing most of the functionality of DSGE model instances. The module also contains the TSDataBase
  6. class which was supposed to be an advanced data carrier class to be passed to the DSGE model instance for estimation
  7. purposes, but this is deprecated and will probably be replaced with a pandas data frame in the near future.
  8. .. moduleauthor:: Eric M. Scheffel <eric.scheffel@nottingham.edu.cn>
  9. '''
  10. #from __future__ import division
  11. import copy
  12. from copy import deepcopy
  13. import os
  14. import sys
  15. import re
  16. from helpers import now_is
  17. import numpy as np
  18. from numpy import matlib as mat
  19. from scipy.linalg import eig as scipyeig
  20. from scipy import optimize
  21. from tempfile import mkstemp
  22. import time
  23. import datetime
  24. import glob
  25. # Import Sympycore, now comes supplied and installed with pymaclab
  26. import sympycore
  27. # Import PP, now comes supplied and installed with pymaclab
  28. import pp
  29. #NOTE: Imports from the refactor
  30. from pymaclab.filters._hpfilter import hpfilter
  31. from pymaclab.filters._bkfilter import bkfilter
  32. from ..stats.var import VAR
  33. from solvers.steadystate import SSsolvers, ManualSteadyState, Fsolve
  34. from parsers._modparser import parse_mod
  35. from parsers._dsgeparser import populate_model_stage_one,populate_model_stage_one_a,\
  36. populate_model_stage_one_b,populate_model_stage_one_bb,populate_model_stage_two
  37. from updaters.one_off import Updaters, dicwrap, dicwrap_deep, listwrap, matwrap
  38. from updaters.queued import Updaters_Queued, dicwrap_queued, dicwrap_deep_queued, listwrap_queued,\
  39. matwrap_queued, Process_Queue, queue
  40. #Define a list of the Greek Alphabet for Latex
  41. greek_alph = ['alpha','beta','gamma','delta','epsilon',
  42. 'varepsilon','zeta','eta','theta','vartheta',
  43. 'iota','kappa','lambda','mu','nu','xi','pi',
  44. 'varpi','rho','varrho','sigma','varsigma',
  45. 'tau','upsilon','phi','varphi','chi','psi',
  46. 'omega','Gamma','Delta','Theta','Lambda',
  47. 'Xi','Pi','Sigma','Upsilon','Phi','Psi','Omega']
  48. # Setting paths
  49. root = ''
  50. modfpath = ''
  51. datapath = ''
  52. browserpath = ''
  53. txtedpath = ''
  54. texedpath = ''
  55. pyedpath = ''
  56. pdfpath = ''
  57. lapackpath = ''
  58. lapackname = ''
  59. mlabpath = ''
  60. # Is matlab installed and can it therefore be used?
  61. #TODO: this is not safe at all if matlab is not installed
  62. try:
  63. import mlabraw
  64. use_matlab = True
  65. except:
  66. use_matlab = False
  67. sess1 = None
  68. # Open mlab session
  69. if use_matlab:
  70. sess1 = mlabraw.open('matlab - nojvm -nosplash')
  71. # Shell class for derivatives branch
  72. class Derivatives(object):
  73. pass
  74. # Shell class for derivatives branch
  75. class Inits(object):
  76. def __init__(self,other=None):
  77. self._other = other
  78. def init1(self,no_wrap=False):
  79. '''
  80. The init1 method. Model population proceeds from the __init__function here. In particular the data gets read in
  81. (not implemented at the moment) and the model parsing begins.
  82. .. note::
  83. The other.vardic gets created and the manual as well as
  84. the numerical steady state sections gets parsed and attached to the DSGE model instance. So the most import fields created
  85. here using function populate_model_stage_one() are:
  86. * other.vardic - variable names dictionary with transform and filtering info
  87. * other.mod_name - short name of the DSGE model from mod file
  88. * other.mod_desc - longer model description from mod file
  89. * other.paramdic - dic of defined parameters with their numerical values
  90. * other.manss_sys - list of equations from the closed form steady state section
  91. * other.ssys_list - list of equations from the numerical steady state section
  92. Also the updaters and updaters_queued branches are opened here and the other.vardic gets wrapped for dynamic updating
  93. behaviour.
  94. :param other: The DSGE model instance itother.
  95. :type other: dsge_inst
  96. '''
  97. other = self._other
  98. initlev = other._initlev
  99. ncpus = other._ncpus
  100. mk_hessian = other._mk_hessian
  101. mesg = other._mesg
  102. # Attach the data from database
  103. if other.dbase != None:
  104. other.getdata(dbase=other.dbase)
  105. if mesg: print "INIT: Instantiating DSGE model with INITLEV="+str(initlev)+" and NCPUS="+str(ncpus)+"..."
  106. if mesg: print "INIT: Attaching model properties to DSGE model instance..."
  107. # Create None tester regular expression
  108. # _nreg = '^\s*None\s*$'
  109. # nreg = re.compile(_nreg)
  110. if mesg: print "INIT: Parsing model file into DSGE model instance..."
  111. txtpars = parse_mod(other.modfile)
  112. other.txtpars = txtpars # do we need txtpars attached for anything else?
  113. secs = txtpars.secs # do we need txtpars attached for anything else?
  114. if mesg: print "INIT: Extraction of info into DSGE model instance Stage [1]..."
  115. # Initial population method of model, does NOT need steady states
  116. other = populate_model_stage_one(other, secs)
  117. if not no_wrap:
  118. # Open updaters path
  119. other.updaters = Updaters()
  120. # Open the updaters_queued path
  121. other.updaters_queued = Updaters_Queued()
  122. # Add the empty queue
  123. other.updaters_queued.queue = queue
  124. # Wrap the vardic
  125. other.updaters.vardic = dicwrap_deep(other,'self.vardic',initlev)
  126. other.updaters_queued.vardic = dicwrap_deep_queued(other,'self.vardic',initlev)
  127. def init1a(self):
  128. '''
  129. The init1a method. Model population proceeds from the init1 method here. The only field which get created here is the raw
  130. (i.e. unsubstituted) substitution dictionary.
  131. .. note::
  132. Field which are created here using the function populate_model_stage_one_a() which in turn calls mk_subs_dic():
  133. * other.nlsubs_raw1 - a list of the @items and their replacements
  134. * other.nlsubsdic - the above just expressed as a keyed list
  135. :param other: The DSGE model instance itother.
  136. :type other: dsge_inst
  137. '''
  138. other = self._other
  139. mesg = other._mesg
  140. secs = other.txtpars.secs
  141. # This is an additional populator which creates subsitution dictionary
  142. # and uses this to already get rid of @s in the manuall sstate list
  143. if mesg: print "INIT: Extraction of info into DSGE model instance Stage [2]..."
  144. # This function only creates the raw substitution dictionary and list from modfile
  145. other = populate_model_stage_one_a(other,secs)
  146. def init1b(self,no_wrap=False):
  147. '''
  148. The init1b method. Model population proceeds from the init1a method here.
  149. .. note::
  150. The only thing which gets done here purposefully *after* calling init1a() is to wrap these fields:
  151. * other.nlsubsdic - the above just expressed as a keyed list
  152. * other.paramdic - dic of defined parameters with their numerical values
  153. in order to give them dynamic updating behaviour. No more is done in this init method call.
  154. :param other: The DSGE model instance itother.
  155. :type other: dsge_inst
  156. '''
  157. other = self._other
  158. initlev = other._initlev
  159. secs = other.txtpars.secs
  160. other = populate_model_stage_one_b(other,secs)
  161. if not no_wrap:
  162. # Wrap the nlsubsdic
  163. if 'nlsubsdic' in dir(other): other.updaters_queued.nlsubsdic = dicwrap_queued(other,'self.nlsubsdic',initlev)
  164. # Wrap the paramdic
  165. other.updaters_queued.paramdic = dicwrap_queued(other,'self.paramdic',initlev)
  166. # Wrap the nlsubsdic
  167. if 'nlsubsdic' in dir(other): other.updaters.nlsubsdic = dicwrap(other,'self.nlsubsdic',initlev)
  168. # Wrap the paramdic
  169. other.updaters.paramdic = dicwrap(other,'self.paramdic',initlev)
  170. def init1c(self,no_wrap=False):
  171. '''
  172. The init1c method. Model population proceeds from the init1b method here. We call populate_model_stage_one_bb() which does quite
  173. a bit of substitution/replacement of the @-prefixed variables. It does this in the numerical and closed form steady state
  174. calculation sections, as well as in the actual FOCs themselves.
  175. .. note::
  176. *After* that we can wrap various fields for updating which are:
  177. * other.foceqs - list of firs-order conditions with @ replacements done
  178. * other.manss_sys - the list of equations from closed form SS section
  179. * other.syss_list - the list of equations from numerical SS section
  180. Notice also that here we replace or generate fields in case the FOCs are supposed to be used
  181. directly in SS calculation because the "use_focs" parameter was not passed empty.
  182. :param other: The DSGE model instance itother.
  183. :type other: dsge_inst
  184. '''
  185. other = self._other
  186. initlev = other._initlev
  187. secs = other.txtpars.secs
  188. mesg = other._mesg
  189. if mesg: print "INIT: Substituting out @ variables in steady state sections..."
  190. other = populate_model_stage_one_bb(other,secs)
  191. if not no_wrap:
  192. if 'foceqs' in dir(other):
  193. # Wrap foceqs
  194. other.updaters.foceqs = listwrap(other,'self.foceqs',initlev)
  195. # Wrap foceqs
  196. other.updaters_queued.foceqs = listwrap(other,'self.foceqs',initlev)
  197. # Allow for numerical SS to be calculated using only FOCs
  198. if other._use_focs and other._ssidic != None:
  199. list_tmp = []
  200. for elem in other._use_focs:
  201. list_tmp.append(other.foceqss[elem])
  202. other.ssys_list = deepcopy(list_tmp)
  203. # Check if this has not already been produced in populate_model_stage_one_bb, chances are it has
  204. if 'ssidic' not in dir(other): other.ssidic = copy.deepcopy(other._ssidic)
  205. if not no_wrap:
  206. # Wrap manss_sys
  207. if 'manss_sys' in dir(other):
  208. other.updaters.manss_sys = listwrap(other,'self.manss_sys',initlev)
  209. other.updaters_queued.manss_sys = listwrap_queued(other,'self.manss_sys',initlev)
  210. # Wrap ssys_list
  211. if 'ssys_list' in dir(other):
  212. other.updaters.ssys_list = listwrap(other,'self.ssys_list',initlev)
  213. other.updaters_queued.ssys_list = listwrap_queued(other,'self.ssys_list',initlev)
  214. def init2(self):
  215. '''
  216. The init2 method. Model population proceeds from the init1c method here. In this initialisation method
  217. the only thing which is being done is to open up the sssolvers branch and pass down required objects to
  218. the manuals closed from solver and the numerical root-finding solver depending on whether information for
  219. this has been included in the DSGE model file. *No* attempt is made at solving for the steady state, the
  220. respective solvers are only being *prepared*.
  221. :param other: The DSGE model instance itother.
  222. :type other: dsge_inst
  223. '''
  224. other = self._other
  225. mesg = other._mesg
  226. secs = other.txtpars.secs
  227. initlev = other._initlev
  228. if mesg: print "INIT: Preparing DSGE model instance for steady state solution..."
  229. # Attach the steady state class branch, and add Fsolve if required but do no more !
  230. other.sssolvers = SSsolvers()
  231. # check if the Steady-State Non-Linear system .mod section has an entry
  232. if all([False if 'None' in x else True for x in secs['manualss'][0]]) or other._use_focs:
  233. intup = (other.ssys_list,other.ssidic,other.paramdic)
  234. other.sssolvers.fsolve = Fsolve(intup,other=other)
  235. # check if the Steady-State Non-Linear system closed form has an entry
  236. # we do this here with ELIF because we only want to set this up for solving if manualss does not exist
  237. elif all([False if 'None' in x else True for x in secs['closedformss'][0]]):
  238. alldic = {}
  239. alldic.update(other.paramdic)
  240. intup = (other.manss_sys,alldic)
  241. other.sssolvers.manss = ManualSteadyState(intup,other=other)
  242. def init3(self):
  243. '''
  244. Initialisation method init3 is quite complex and goes through a number of logical tests in order to determine
  245. how to solve for the steady state of the model.
  246. .. note::
  247. There are 7 different ways a DSGE model can obtain its steady state solution depending on what information has
  248. been provided:
  249. 1) The steady state values dictionary has been passed as argument, then init3() will NEVER be called
  250. 2) Information has been provided using the "use_focs" parameter to use FOCs directly, externally passed using use_focs
  251. 3) Information has been provided using the "use_focs" parameter to use FOCs directly, but inside model file
  252. 4) Information has only been provided in the numerical SS section
  253. 5) Information has only been provided in the closed form SS section
  254. 6) Both CF-SS and NUM-SS info are present and NUM-SS is subset if CF-SS
  255. 7) Both CF-SS and NUM-SS info are present and CF is residual
  256. These options are better explained in the documentation to PyMacLab in the steady state solver section.
  257. :param other: The DSGE model instance itother.
  258. :type other: dsge_inst
  259. '''
  260. other = self._other
  261. txtpars = other.txtpars
  262. secs = txtpars.secs
  263. initlev = other._initlev
  264. ################################## STEADY STATE CALCULATIONS !!! #######################################
  265. if other._mesg: print "INIT: Attempting to find DSGE model's steady state automatically..."
  266. # ONLY NOW try to solve !
  267. ##### OPTION 1: There is only information externally provided and we are using FOCs
  268. if other._use_focs and other._ssidic != None:
  269. if '_internal_focs_used' not in dir(other):
  270. if other._mesg: print "SS: Using FOCs and EXTERNALLY supplied information...attempting to solve SS..."
  271. else:
  272. ##### OPTION 1b: There is only information internally provided and we are using FOCs
  273. if other._mesg: print "SS: Using FOCs and INTERNALLY supplied information...attempting to solve SS..."
  274. del other._internal_focs_used
  275. other.sssolvers.fsolve.solve()
  276. if other.sssolvers.fsolve.ier == 1:
  277. other.sstate = deepcopy(other.sssolvers.fsolve.fsout)
  278. other.switches['ss_suc'] = ['1','1']
  279. if other._mesg: print "INIT: Steady State of DSGE model found (SUCCESS)..."
  280. else:
  281. other.switches['ss_suc'] = ['1','0']
  282. if other._mesg: print "INIT: Steady State of DSGE model not found (FAILURE)..."
  283. return
  284. ##### OPTION 2: There is only information provided in the numerical section NOT in closed-form
  285. if not all([False if 'None' in x else True for x in secs['closedformss'][0]]) and\
  286. all([False if 'None' in x else True for x in secs['manualss'][0]]) and not other._use_focs and other._ssidic == None:
  287. if other._mesg: print "SS: ONLY numerical steady state information supplied...attempting to solve SS..."
  288. other.sssolvers.fsolve.solve()
  289. ##### OPTION 3: There is only information on closed-form steady state, BUT NO info on numerical steady state
  290. # Solve using purely closed form solution if no other info on model is available
  291. if all([False if 'None' in x else True for x in secs['closedformss'][0]]) and\
  292. not all([False if 'None' in x else True for x in secs['manualss'][0]]):
  293. if other._mesg: print "SS: ONLY CF-SS information supplied...attempting to solve SS..."
  294. alldic = {}
  295. alldic.update(other.paramdic)
  296. intup = (other.manss_sys,alldic)
  297. other.sssolvers.manss = ManualSteadyState(intup)
  298. other.sssolvers.manss.solve()
  299. other.sstate = {}
  300. other.sstate.update(other.sssolvers.manss.sstate)
  301. ##### OPTION 4: There is information on closed-form AND on numerical steady state
  302. # Check if the numerical and closed form sections have entries
  303. if all([False if 'None' in x else True for x in secs['manualss'][0]]) and\
  304. all([False if 'None' in x else True for x in secs['closedformss'][0]]):
  305. # Create unordered Set of closed from solution variables
  306. manss_set = set()
  307. for elem in other.manss_sys:
  308. manss_set.add(elem.split('=')[0].lstrip().rstrip())
  309. # If ssidic is not empty we need to make sure it perfectly overlaps with manss_set in order to replace ssidic
  310. if other.ssidic != {}:
  311. numss_set = set()
  312. for keyo in other.ssidic.keys():
  313. numss_set.add(keyo)
  314. ##### OPTION 4a: If there is an ssidic and its keys are subset of manss_set, the use as suggestion for new ssi_dic
  315. if numss_set.issubset(manss_set):
  316. if other._mesg: print "SS: CF-SS and NUM-SS (overlapping) information information supplied...attempting to solve SS..."
  317. alldic = {}
  318. alldic.update(other.paramdic)
  319. intup = (other.manss_sys,alldic)
  320. other.sssolvers.manss = ManualSteadyState(intup)
  321. other.sssolvers.manss.solve()
  322. for keyo in other.ssidic.keys():
  323. other.ssidic[keyo] = other.sssolvers.manss.sstate[keyo]
  324. ##### OPTION 4b: ssidic is empty, so we have to assumed that the variables in closed form are suggestions for ssidic
  325. # If it is empty, then just compute the closed form SS and pass to ssidic as starting value
  326. elif other.ssidic == {}:
  327. if other._mesg: print "SS: CF-SS and NUM-SS (empty ssdic) information information supplied...attempting to solve SS..."
  328. alldic = {}
  329. alldic.update(other.paramdic)
  330. intup = (other.manss_sys,alldic)
  331. other.sssolvers.manss = ManualSteadyState(intup)
  332. other.sssolvers.manss.solve()
  333. other.ssidic.update(other.sssolvers.manss.sstate)
  334. # Test at least if the number of ssidic vars equals number of equations
  335. if len(other.ssidic.keys()) != len(other.ssys_list):
  336. print "Error: Number of variables in initial starting values dictionary != number of equations"
  337. sys.exit()
  338. ######## Finally start the numerical root finder with old or new ssidic from above
  339. if all([False if 'None' in x else True for x in secs['manualss'][0]]) and\
  340. not all([False if 'None' in x else True for x in secs['closedformss'][0]]) and not other._use_focs:
  341. other.sssolvers.fsolve.solve()
  342. elif all([False if 'None' in x else True for x in secs['manualss'][0]]) and\
  343. all([False if 'None' in x else True for x in secs['closedformss'][0]]) and not other._use_focs:
  344. other.sssolvers.fsolve.solve()
  345. if all([False if 'None' in x else True for x in secs['manualss'][0]]):
  346. if other.sssolvers.fsolve.ier == 1:
  347. other.sstate = other.sssolvers.fsolve.fsout
  348. other.numssdic = other.sssolvers.fsolve.fsout
  349. # Attach solutions to intial variable dictionaries, for further analysis
  350. other.ssidic_modfile = deepcopy(other.ssidic)
  351. # Update old ssidic with found solutions
  352. other.ssidic = deepcopy(other.sssolvers.fsolve.fsout)
  353. other.sssolvers.fsolve.ssi = other.ssidic
  354. other.switches['ss_suc'] = ['1','1']
  355. if other._mesg: print "INIT: Steady State of DSGE model found (SUCCESS)..."
  356. else:
  357. other.switches['ss_suc'] = ['1','0']
  358. if other._mesg: print "INIT: Steady State of DSGE model not found (FAILURE)..."
  359. ########## Here we are trying to merge numerical SS solver's results with result closed-form calculations, if required
  360. if all([False if 'None' in x else True for x in secs['manualss'][0]]) and\
  361. all([False if 'None' in x else True for x in secs['closedformss'][0]]):
  362. if other._mesg: print "INIT: Merging numerical with closed form steady state if needed..."
  363. # Check for existence of closedform AND numerical steady state
  364. # We need to stop the model instantiation IFF numerical solver was attempted but failed AND closed form solver depends on it.
  365. if all([False if 'None' in x else True for x in secs['closedformss'][0]]) and\
  366. all([False if 'None' in x else True for x in secs['manualss'][0]]) and other.ssidic != {}:
  367. if other.switches['ss_suc'] == ['1','0']:
  368. print "ERROR: You probably want to use numerical steady state solution to solve for RESIDUAL closed form steady states."
  369. print "However, the numerical steady state solver FAILED to find a root, so I am stopping model instantiation here."
  370. sys.exit()
  371. ##### OPTION 5: We have both numerical and (residual) closed form information
  372. # Check if a numerical SS solution has been attempted and succeeded, then take solutions in here for closed form.
  373. elif other.switches['ss_suc'] == ['1','1'] and not numss_set.issubset(manss_set):
  374. if other._mesg: print "SS: CF-SS (residual) and NUM-SS information information supplied...attempting to solve SS..."
  375. alldic = {}
  376. alldic.update(other.sstate)
  377. alldic.update(other.paramdic)
  378. intup = (other.manss_sys,alldic)
  379. other.sssolvers.manss = ManualSteadyState(intup)
  380. other.sssolvers.manss.solve()
  381. # Here merging takes place
  382. other.sstate.update(other.sssolvers.manss.sstate)
  383. # Double check if no steady state values are negative, as LOGS may have to be taken.
  384. if 'sstate' in dir(other):
  385. for keyo in other.sstate.keys():
  386. if '_bar' in keyo and float(other.sstate[keyo]) < 0.0:
  387. print "WARNING: Steady state value "+keyo+ " is NEGATIVE!"
  388. print "This is very likely going to either error out or produce strange results"
  389. print "Re-check your model declarations carefully!"
  390. ######################################### STEADY STATE CALCULATION SECTION DONE ##################################
  391. def init4(self,no_wrap=False):
  392. '''
  393. This model instance sub-initializor only calls the section which use the computed steady state
  394. in order to compute derivatives and open dynamic solver branches on the instance. But Jacobian and Hessian
  395. are *not* computed here, this is postponed to the next init level.
  396. .. note::
  397. The following last field is wrapped for dynamic execution:
  398. * other.sigma - the variance-covariance matrix of the iid shocks
  399. Notice that after wrapping this last field the process_queue class is instantiated at last,
  400. because it needs to have access to *all* of the wrapped fields. Also in this method, the function
  401. populate_model_stage_two() is called which prepares the nonlinear FOCs for derivative-taking.
  402. :param other: The DSGE model instance itother.
  403. :type other: dsge_inst
  404. '''
  405. other = self._other
  406. txtpars = other.txtpars
  407. secs = txtpars.secs
  408. initlev = other._initlev
  409. ncpus = other._ncpus
  410. mk_hessian = other._mk_hessian
  411. mesg = other._mesg
  412. if mesg: print "INIT: Preparing DSGE model instance for computation of Jacobian and Hessian..."
  413. # Now populate more with stuff that needs steady state
  414. other = populate_model_stage_two(other, secs)
  415. if not no_wrap:
  416. # Need to wrap variance covariance matrix here
  417. other.updaters.sigma = matwrap(other,'self.sigma',initlev)
  418. # Need to wrap variance covariance matrix here
  419. other.updaters_queued.sigma = matwrap_queued(other,'self.sigma',initlev)
  420. ####### All queued updaters initialized, no add processing instance
  421. # Add the queue process instance
  422. other.updaters_queued.process_queue = Process_Queue(other=other)
  423. def init5(self,update=False):
  424. '''
  425. This model instance initialisation step is the last substantial one in which the dynamic solution of the DSGE
  426. model instance is finally computed using a choice of methods which can be called at runtime.
  427. :param other: The DSGE model instance itother.
  428. :type other: dsge_inst
  429. '''
  430. other = self._other
  431. other.derivatives = Derivatives()
  432. txtpars = other.txtpars
  433. secs = txtpars.secs
  434. initlev = other._initlev
  435. ncpus = other._ncpus
  436. mk_hessian = other._mk_hessian
  437. mesg = other._mesg
  438. #TODO: delay above and only import if needed
  439. from solvers.modsolvers import MODsolvers
  440. # Open the model solution tree branch
  441. other.modsolvers = MODsolvers()
  442. ######################## LINEAR METHODS !!! ############################
  443. # see if there are any log-linearized equations
  444. if all([False if 'None' in x else True for x in secs['modeq'][0]]):
  445. from solvers.modsolvers import PyUhlig, MatUhlig, MatKlein, MatKleinD, ForKlein
  446. if mesg: print "INIT: Computing DSGE model's log-linearized solution using Uhlig's Toolbox..."
  447. # Open the matlab Uhlig object
  448. intup = ((other.nendo,other.ncon,other.nexo),
  449. other.eqindx,
  450. other.vreg,
  451. other.llsys_list,
  452. other.diffli1,
  453. other.diffli2,
  454. sess1,
  455. other.vardic)
  456. other.modsolvers.matuhlig = MatUhlig(intup)
  457. # Open the native Uhlig object
  458. intup = ((other.nendo,other.ncon,other.nexo),
  459. other.eqindx,
  460. other.vreg,
  461. other.llsys_list,
  462. other.diffli1,
  463. other.diffli2,
  464. sess1)
  465. other.modsolvers.pyuhlig = PyUhlig(intup)
  466. # Open the matlab Klein object
  467. intup = ((other.nendo,other.ncon,other.nexo),
  468. other.eqindx,
  469. other.vreg,
  470. other.llsys_list,
  471. other.diffli1,
  472. other.diffli2,
  473. sess1)
  474. other.modsolvers.matklein = MatKlein(intup)
  475. # Open the Fortran Klein object
  476. intup = ((other.nendo,other.ncon,other.nexo),
  477. other.eqindx,
  478. other.vreg,
  479. other.llsys_list,
  480. other.diffli1,
  481. other.diffli2,
  482. sess1)
  483. other.modsolvers.forklein = ForKlein(intup)
  484. ################## 1ST-ORDER NON-LINEAR METHODS !!! ##################
  485. if all([False if 'None' in x else True for x in secs['focs'][0]]):
  486. from solvers.modsolvers import (MatWood, ForKleinD)
  487. if not update:
  488. if ncpus > 1 and mk_hessian:
  489. if mesg: print "INIT: Computing DSGE model's Jacobian and Hessian using parallel approach..."
  490. other.mkjahepp()
  491. elif ncpus > 1 and not mk_hessian:
  492. if mesg: print "INIT: Computing DSGE model's Jacobian using parallel approach..."
  493. other.mkjahepp()
  494. else:
  495. if mesg: print "INIT: Computing DSGE model's Jacobian and Hessian using serial approach..."
  496. other.mkjahe()
  497. else:
  498. if ncpus > 1 and mk_hessian:
  499. if mesg: print "INIT: Computing DSGE model's Jacobian and Hessian using parallel approach..."
  500. other.mkjaheppn()
  501. elif ncpus > 1 and not mk_hessian:
  502. if mesg: print "INIT: Computing DSGE model's Jacobian using parallel approach..."
  503. other.mkjaheppn()
  504. else:
  505. if mesg: print "INIT: Computing DSGE model's Jacobian and Hessian using serial approach..."
  506. other.mkjahen()
  507. # Check if the obtained matrices A and B have correct dimensions
  508. if other.derivatives.jAA.shape[0] != other.derivatives.jAA.shape[1]:
  509. print "ERROR: Matrix A of derivatives does not have #vars=#equations"
  510. if other.derivatives.jBB.shape[0] != other.derivatives.jBB.shape[1]:
  511. print "ERROR: Matrix B of derivatives does not have #vars=#equations"
  512. # Open the MatWood object
  513. intup = (other.derivatives.jAA,other.derivatives.jBB,
  514. other.nexo,other.ncon,
  515. other.nendo,sess1)
  516. other.modsolvers.matwood = MatWood(intup)
  517. # Make the AA and BB matrices as references available instead
  518. other.modsolvers.matwood.jAA = other.derivatives.jAA
  519. other.modsolvers.matwood.jBB = other.derivatives.jBB
  520. # Open the Fortran KleinD object
  521. if 'nlsubsys' in dir(other):
  522. intup = (other.derivatives.numj,
  523. other.nendo,other.nexo,
  524. other.ncon,other.sigma,
  525. other.derivatives.jAA,other.derivatives.jBB,
  526. other.vardic,other.vdic,
  527. other.mod_name,other.audic,
  528. other.derivatives.numjl,
  529. other.nother)
  530. else:
  531. intup = (other.derivatives.numj,
  532. other.nendo,other.nexo,
  533. other.ncon,other.sigma,
  534. other.derivatives.jAA,other.derivatives.jBB,
  535. other.vardic,other.vdic,
  536. other.mod_name,other.audic)
  537. other.modsolvers.forkleind = ForKleinD(intup,other=other)
  538. # Make the AA and BB matrices as references available instead
  539. other.modsolvers.forkleind.A = other.derivatives.jAA
  540. other.modsolvers.forkleind.B = other.derivatives.jBB
  541. ################## 2ND-ORDER NON-LINEAR METHODS !!! ##################
  542. if all([False if 'None' in x else True for x in secs['vcvm'][0]]) and 'numh' in dir(other.derivatives):
  543. from solvers.modsolvers import (PyKlein2D, MatKlein2D)
  544. # Open the MatKlein2D object
  545. if 'nlsubsys' in dir(other):
  546. intup = (other.derivatives.numj,other.derivatives.numh,
  547. other.nendo,other.nexo,
  548. other.ncon,other.sigma,
  549. other.derivatives.jAA,other.derivatives.jBB,
  550. other.vardic,other.vdic,
  551. other.mod_name,other.audic,
  552. other.derivatives.numjl,other.derivatives.numhl,
  553. other.nother,sess1)
  554. else:
  555. intup = (other.derivatives.numj,other.derivatives.numh,
  556. other.nendo,other.nexo,
  557. other.ncon,other.sigma,
  558. other.derivatives.jAA,other.derivatives.jBB,
  559. other.vardic,other.vdic,
  560. other.mod_name,other.audic,
  561. sess1)
  562. other.modsolvers.matklein2d = MatKlein2D(intup)
  563. # Make the AA and BB matrices as references available instead
  564. other.modsolvers.matklein2d.A = other.derivatives.jAA
  565. other.modsolvers.matklein2d.B = other.derivatives.jBB
  566. # Open the PyKlein2D object, but don't pass mlabwrap session
  567. intup = intup[:-1]
  568. other.modsolvers.pyklein2d = PyKlein2D(intup,other=other)
  569. # Make the AA and BB matrices as references available instead
  570. other.modsolvers.pyklein2d.A = other.derivatives.jAA
  571. other.modsolvers.pyklein2d.B = other.derivatives.jBB
  572. other.modsolvers.pyklein2d.forkleind.A = other.derivatives.jAA
  573. other.modsolvers.pyklein2d.forkleind.B = other.derivatives.jBB
  574. def init_out(self):
  575. '''
  576. The final intializor section does some extra stuff after all has been done.
  577. :param self: The DSGE model instance itself.
  578. :type self: dsge_inst
  579. '''
  580. other = self._other
  581. initlev = other._initlev
  582. # Make sure the jobserver has done his jobs
  583. jobserver.wait()
  584. # Empty locdic for the locate helper function
  585. locdic = {}
  586. ##################THE DSGE MODEL CLASS (WORKS)#####################
  587. class DSGEmodel(object):
  588. '''
  589. This is the macrolab DSGEmodel class. It is the main class of the packages and instantiates
  590. DSGE model instances which possess many features such as model file parsers, solvers, etc. The __init__
  591. function first called mostly attaches the passed arguments to the DSGE model instance in form of private _X
  592. data fields.
  593. .. note::
  594. Notice that the various init method, i.e. init1, init1a, etc. are not called here, but they are called externally in the
  595. pymaclab package when instantiating a new DSGE model using pymaclab.newMOD().
  596. :param ffile: The absolute path to the PyMacLab DSGE model file to be parsed on instantiation
  597. :type ffile: str
  598. :param dbase: A database with time series data for estimation purposes. Not implemented at the moment
  599. :type standard: unknown
  600. :param initlev: Takes values 0,1,2. Determines how deep the instantiation cascades through all methods.
  601. If 0 then the file is parsed and the instance is only *prepared* for steady state solving.
  602. If 1 then the file is parsed, the SS is automatically computed and instance is *prepared*
  603. for dynamic solving. If 2 then the instance is solved all the way.
  604. :type initlev: int
  605. :param mesg: If True then lots of diagnostics are printed to the screen during instantiation.
  606. :type mesg: bool
  607. :param ncpus: The number of CPU core to be employed, defaults to 1. But 'auto' can also be used for detection
  608. :type ncpus: int|str
  609. :param mk_hessian: Whether the Hessian should be computed as this is expensive.
  610. :type mk_hessian: bool
  611. :param use_focs: Should the FOCs be used directly to look for the steady state? Must use list|tuple to pick equations.
  612. :type use_focs: tuple
  613. :param ssidic: A Python ssidic with the initial starting values for solving for the SS numerically
  614. :type ssidic: dic
  615. :param sstate: A Python ssidic with the externally computed steady state values of the model
  616. :type sstate: dic
  617. :return self: *(dsge_inst)* - A populated DSGE model instance with fields and methods
  618. '''
  619. # Instantiate a global class jobserver accessible from all instances
  620. global ppservers, jobserver
  621. ppservers = ()
  622. jobserver = pp.Server(ppservers=ppservers)
  623. # Initializes the absolute basics, errors difficult to occur
  624. def __init__(self,ffile=None,dbase=None,initlev=2,mesg=False,ncpus='auto',mk_hessian=True,\
  625. use_focs=False,ssidic=None,sstate=None,vtiming={'exo':[-1,0],'endo':[-1,0],'con':[0,1]}):
  626. # Open the inits branch
  627. self.inits = Inits(other=self)
  628. # Make sure the jobserver has done his global jobs each time you instantiate a new instance
  629. jobserver.wait()
  630. if sstate != None:
  631. self._sstate = deepcopy(sstate)
  632. self._initlev = initlev #TODO: remove this as an option
  633. self._vtiming = vtiming
  634. self._mesg = mesg
  635. self._ncpus = ncpus
  636. self._mk_hessian = mk_hessian
  637. self._use_focs = use_focs
  638. self._ssidic = ssidic
  639. if self._use_focs and self._ssidic != None:
  640. self.ssidic = deepcopy(self._ssidic)
  641. # Set no author
  642. self.setauthor()
  643. # Open the switches dic and initiate
  644. #NOTE this is _not_ pythonic
  645. self.switches = {}
  646. self.switches['errocc'] = '0'
  647. self.switches['ss_suc'] = ['0','0']
  648. # Attach some model attributes
  649. self.modfile = ffile
  650. self.dbase = dbase
  651. def mk_dynare(self,order=1,centralize=False,fpath=None,focli=None):
  652. # Import the template and other stuff
  653. from pymaclab.modfiles.templates import mako_dynare_template
  654. from scipy import io
  655. from solvers.modsolvers import Dynare
  656. import tempfile
  657. import os
  658. import glob
  659. template_dic = deepcopy(self.template_paramdic)
  660. # Render the template to be passed to dynare++
  661. tmp_dic = {}
  662. tmp_dic['focli'] = focli
  663. template_dic.update(tmp_dic)
  664. modstr = mako_dynare_template.render(**template_dic)
  665. # If a filepath has been passed then just write the Dynare++ modfile, but no more!
  666. if fpath != None:
  667. filo = open(fpath,'w')
  668. filo.write(modstr)
  669. filo.flush()
  670. filo.close()
  671. return
  672. filo = tempfile.NamedTemporaryFile()
  673. fname = filo.name
  674. fname2 = fname.split('/')[-1]
  675. filo2 = open(os.path.join(os.getcwd(),'test.mod'),'w')
  676. filo2.write(modstr)
  677. filo2.flush()
  678. filo2.close()
  679. filo.file.write(modstr)
  680. filo.file.flush()
  681. self.modsolvers.dynare = Dynare({})
  682. self.modsolvers.dynare.__setattr__('modfile',modstr)
  683. if not centralize:
  684. os.system('dynare++ --no-centralize --order '+str(order)+' '+filo.name)
  685. else:
  686. os.system('dynare++ --order '+str(order)+' '+filo.name)
  687. dynret = io.loadmat(os.path.join(os.getcwd(),filo.name.split('/')[-1]+'.mat'))
  688. # Check if solution has been computed and attache all solution matrices to dynare instance
  689. if dynret.has_key('dyn_g_1'):
  690. self.modsolvers.dynare.__init__(dynret)
  691. else:
  692. print "FAIL: Dynare could not determine solution."
  693. filo.close()
  694. # Delete all of the files
  695. for filor in glob.glob(fname2+'*.*'):
  696. os.remove(filor)
  697. # Create some other objects on dynare branch
  698. self.modsolvers.dynare.sstate = {}
  699. self.modsolvers.dynare.dynorder = []
  700. self.modsolvers.dynare.dynsorder = []
  701. for i1,elem in enumerate(self.modsolvers.dynare.dyn_vars):
  702. self.modsolvers.dynare.dynorder.append(str(elem).strip()+'(t)')
  703. if str(elem).strip()+'_bar' in self.sstate.keys():
  704. self.modsolvers.dynare.sstate[str(elem).strip()+'_bar'] = self.modsolvers.dynare.dyn_ss[i1,0]
  705. for i1,elem in enumerate(self.modsolvers.dynare.dyn_state_vars):
  706. self.modsolvers.dynare.dynsorder.append(str(elem).strip()+'(t)')
  707. # Create P and F matrix reflecting ordering of pymaclab
  708. P = np.matlib.zeros([self.nstat,self.nstat])
  709. F = np.matlib.zeros([self.ncon,self.nstat])
  710. listo = [x[0] for x in self.vdic['exo']]+[x[0] for x in self.vdic['endo']]
  711. for i1,elem in enumerate(listo):
  712. for i2,elem2 in enumerate(listo):
  713. P[i1,i2] = self.modsolvers.dynare.dyn_g_1[self.modsolvers.dynare.dynorder.index(elem),self.modsolvers.dynare.dynsorder.index(elem2)]
  714. for i1,elem in enumerate([x[0] for x in self.vdic['con']]):
  715. for i2,elem2 in enumerate(listo):
  716. F[i1,i2] = self.modsolvers.dynare.dyn_g_1[self.modsolvers.dynare.dynorder.index(elem),self.modsolvers.dynare.dynsorder.index(elem2)]
  717. self.modsolvers.dynare.PP = deepcopy(P)
  718. self.modsolvers.dynare.FF = deepcopy(F)
  719. def find_rss(self,mesg=False,rootm='hybr',scale=0.0):
  720. '''
  721. The is a method which can be called to find the risky steady state
  722. :param self: The DSGE model instance itself.
  723. :type self: dsge_inst
  724. '''
  725. varbar = []
  726. nexo = self.nexo
  727. for elem in self.vardic['endo']['var']:
  728. varbar.append(elem[0].split('(')[0]+'_bar')
  729. for elem in self.vardic['con']['var']:
  730. varbar.append(elem[0].split('(')[0]+'_bar')
  731. tmp_dic = {}
  732. tmp_dic.update(self.paramdic)
  733. tmp_dic.update(self.sstate)
  734. sstate_li = []
  735. sstate = {}
  736. for elem in varbar:
  737. if elem in tmp_dic.keys():
  738. sstate[elem] = tmp_dic[elem]
  739. sstate_li.append(tmp_dic[elem])
  740. rsstate_li = deepcopy(sstate_li)
  741. rsstate = deepcopy(sstate)
  742. clone = copy.deepcopy(self)
  743. clone._mesg = False
  744. # Define the function to be handed over
  745. # to fsolve
  746. def func(invar):
  747. '''
  748. ####### Put in a trap in case number turn negative ########
  749. negli = [True if x < 0.0 else False for x in invar]
  750. if any(negli):
  751. for i1,elem in enumerate(negli):
  752. invar[i1] = self.sstate[varbar[i1]]+0.01*self.sstate[varbar[i1]]
  753. ###########################################################
  754. '''
  755. # Update ss with passed values
  756. for i1,elem in enumerate(varbar):
  757. clone.sstate[elem] = invar[i1]
  758. # Update the model's derivatives, but only numerically
  759. clone.init5(update=True)
  760. # Solve the 2nd-order accurate solution
  761. clone.modsolvers.pyklein2d.solve()
  762. # Get retval into right shape
  763. KX = clone.modsolvers.pyklein2d.KX
  764. retval = [float(x) for x in KX[nexo:]]
  765. KY = clone.modsolvers.pyklein2d.KY
  766. retval+=[float(x) for x in KY]
  767. retval = np.array(retval)
  768. if mesg:
  769. print invar
  770. print '------------------------------------'
  771. return retval
  772. # Define the initial values and
  773. # start the non-linear solver
  774. init_val = np.array([float(self.sstate[varbar[i1]])+\
  775. (scale/100.0)*float(self.sstate[varbar[i1]]) for i1 in range(len(varbar))])
  776. outobj = optimize.root(func,init_val,method=rootm)
  777. rss_funcval1 = outobj.fun
  778. output = outobj.x
  779. mesg = outobj.message
  780. ier = outobj.status
  781. self.rss_funcval1 = rss_funcval1
  782. # Check the difference and do bounded minimisation (root-finding)
  783. diff_dic = {}
  784. bounds_dic = {}
  785. for i1,keyo in enumerate(varbar):
  786. if keyo in self.sstate.keys():
  787. if output[i1] != self.sstate[keyo]:
  788. diff_dic[keyo] = output[i1]
  789. bounds_dic[keyo] = (output[i1],output[i1])
  790. if bounds_dic != {}:
  791. # Also add the non-bar SS values to bounds_dic, before doing constrained minimisation
  792. for keyo in self.sstate.keys():
  793. if "_bar" not in keyo: bounds_dic[keyo] = (self.sstate[keyo],self.sstate[keyo])
  794. # Do constrained root finding here
  795. if 'fsolve' in dir(clone.sssolvers):
  796. clone.sssolvers.fsolve.solve(bounds_dic=bounds_dic)
  797. # Also need to make sure residually computed SS get updated accordingly
  798. if 'manss' in dir(clone.sssolvers) and 'fsolve' in dir(clone.sssolvers):
  799. clone.sssolvers.manss.paramdic.update(clone.sssolvers.fsolve.fsout)
  800. clone.sssolvers.manss.solve()
  801. # Run loop one last time to get rss_funval2
  802. if 'fsolve' in dir(clone.sssolvers):
  803. clone.sstate.update(clone.sssolvers.fsolve.fsout)
  804. if 'manss' in dir(clone.sssolvers):
  805. clone.sstate.update(clone.sssolvers.manss.sstate)
  806. clone.init5(update=True)
  807. clone.modsolvers.pyklein2d.solve()
  808. retval = []
  809. # Get retval into right shape
  810. KX = clone.modsolvers.pyklein2d.KX
  811. retval = [float(x) for x in KX]
  812. KY = clone.modsolvers.pyklein2d.KY
  813. for elem in KY:
  814. retval.append(float(elem))
  815. retval = np.array(retval)
  816. self.rss_funcval2 = retval
  817. # Now attach final results to instance
  818. self.rsstate = deepcopy(self.sstate)
  819. self.rparamdic = deepcopy(self.paramdic)
  820. for i1,elem in enumerate(varbar):
  821. if elem in self.rsstate.keys(): self.rsstate[elem] = output[i1]
  822. if elem in self.rparamdic.keys(): self.rparamdic[elem] = output[i1]
  823. if bounds_dic != {}:
  824. if 'fsolve' in dir(clone.sssolvers):
  825. self.rsstate.update(clone.sssolvers.fsolve.fsout)
  826. if 'fsolve' in dir(clone.sssolvers) and 'manss' in dir(clone.sssolvers):
  827. self.rsstate.update(clone.sssolvers.manss.sstate)
  828. # html info of model opened with webbrowser
  829. def info(self):
  830. '''
  831. A convience method for collecting and displaying the model's properties in a browser
  832. using html language.
  833. '''
  834. tmplist = glob.glob('tempz*.html')
  835. for x in tmplist:
  836. os.remove(x)
  837. modname = self.mod_name
  838. secs = self.txtpars.secs
  839. direc = os.getcwd()
  840. fd,fpath = mkstemp(prefix='tempz',suffix='.html',dir=direc)
  841. htmlfile = os.fdopen(fd,'w+b')
  842. htmlfile.write('<HTML><BODY BGCOLOR="white">\n')
  843. htmlfile.write('<H2>%s</H2>\n'%modname)
  844. htmlfile.write('\n\n')
  845. for x1 in secs['info'][1]:
  846. htmlfile.write('<P>'+x1+'\n')
  847. htmlfile.write('<P>'+'<H4>Model Parameters</H4>\n')
  848. for x1 in secs['params'][1]:
  849. htmlfile.write('<P>'+x1+'\n')
  850. htmlfile.write('<P>'+'<H4>Nonlinear First-Order Conditions</H4>\n')
  851. for x1 in secs['focs'][1]:
  852. if x1[0] == '[':
  853. htmlfile.write('<P>'+x1+'\n')
  854. elif x1[0] != '[':
  855. htmlfile.write('<P>'+4*'&nbsp;'+ x1+'\n')
  856. htmlfile.write('<P>'+'<H4>Log-Linearized Model Equations</H4>\n')
  857. for x1 in secs['modeq'][1]:
  858. if x1[0] == '[':
  859. htmlfile.write('<P>'+x1+'\n')
  860. elif x1[0] != '[':
  861. htmlfile.write('<P>'+4*'&nbsp;'+ x1+'\n')
  862. htmlfile.write('</BODY></HTML>\n')
  863. htmlfile.write('<P>'+'<H2>Solution Section</H2>\n')
  864. htmlfile.write('<P>'+'<H4>Uhlig Toolkit Output</H4>\n')
  865. htmlfile.close()
  866. cmd = browserpath+' '+fpath
  867. os.system(cmd)
  868. tmplist = glob.glob('tempz*.html')
  869. for x in tmplist:
  870. os.remove(x)
  871. return 'Model Website opened!'
  872. def setauthor(self,author=None):
  873. '''
  874. Convience method for setting the model's author's name.
  875. '''
  876. if author == None:
  877. self.author = 'No author'
  878. else:
  879. self.author = author
  880. def pdf(self):
  881. '''
  882. This will pdflatex the model's tex file and then view it in the system's pdf viewer.
  883. '''
  884. modfile = self.modfile
  885. modfname = modfile.split('.')[0]
  886. if modfname+'.pdf' in os.listdir(modfpath):
  887. os.remove(os.path.join(modfpath,modfname+'.pdf'))
  888. if modfname+'.log' in os.listdir(modfpath):
  889. os.remove(os.path.join(modfpath,modfname+'.log'))
  890. if modfname+'.aux' in os.listdir(modfpath):
  891. os.remove(os.path.join(modfpath,modfname+'.aux'))
  892. if modfname+'.dvi' in os.listdir(modfpath):
  893. os.remove(os.path.join(modfpath,modfname+'.dvi'))
  894. if modfname+'.log' in os.listdir(os.getcwd()):
  895. os.remove(os.path.join(os.getcwd(),modfname+'.log'))
  896. # Does the model tex file even exist?
  897. if modfname+'.tex' not in os.listdir(modfpath):
  898. print 'Error: The model tex file does not exist!'
  899. print 'Use model.texed() to create a new one!'
  900. return
  901. # First check for Chktex syntax errors
  902. if 'texer.log' in os.listdir(os.getcwd()):
  903. os.remove(os.path.join(os.getcwd(),'texer.log'))
  904. args = '--quiet -v2 -n3 -n25 -n12 -n35'
  905. cmd = 'chktex '+args+' '+os.path.join(modfpath,modfname+'.tex'+' > texer.log')
  906. os.system(cmd)
  907. file = open(os.path.join(os.getcwd(),'texer.log'),'rU')
  908. errlist = file.readlines()
  909. if len(errlist)== 0:
  910. file.close()
  911. os.remove(os.path.join(os.getcwd(),'texer.log'))
  912. else:
  913. print 'There were err…

Large files files are truncated, but you can click here to view the full file