"""
Dynamic Programming.
Created on Mon Feb 15 16:48:37 2021
@author: A.Goumilevski
"""
import os
import numpy as np
import pandas as pd
from time import time
import scipy.sparse as sparse
from scipy.linalg import pinv,norm
from scipy.sparse.linalg import spsolve
from scipy.optimize import fmin
from numpy.matlib import repmat
#from numpy import log, exp, sin, cos, tan, sqrt, pi, inf
# Grids
#from snowdrop.src.numeric.dp.grids import UnstructuredGrid,CartesianGrid,NonUniformCartesianGrid,SmolyakGrid
NITERATIONS = 100 # Maximum nymber of iterations
EPSILON = 1.e-5 # Convergence tolerance
[docs]
class DynProg:
def __init__(self,functions,m_var,m_par,options,state_variables,control_variables,utilities,value_functions,eqs,be_eqs,
lower_boundary,upper_boundary,ns=2,rho=0.8,shk_stddedv=0.3,Ngrid=1000,ngrid=100,n_order=20,R=None,Q=None):
"""
Set up the elements that define an instance of the DP class.
Parameters:
functions : dict
Map of model functions.
m_var : dict
Map of variables names and its intitial values.
m_par : dict
Map of parameters names and values.
options : dict
Model options.
state_variables : list
Names of state variables. These variables are functions of decision rules that depend on
control variables,i.e., state_variables = function(decision_rules(control_variables)).
control_variables : list
Names of control variables. Value function is maximized with respect to these variables.
eqs : list
List of transient equations.
be_eqs : list
List of equations used to calculate value function.
utilities : list
Utility functions.
value_functions : list
Value functions.
lower_boundary : dict
Lower boundary of state variables.
upper_boundary : dict
Upper boundary of state variables.
ns : int, optional
Number of admissable states of shocks. The default is 2.
Shocks are stockastic and are descried by VAR(1) process with 'ns' states.
rho : float, optional
Persistancy of shocks in VAR(1) process. The default is 0.8.
shk_stddedv : float, optional
Standard deviation of shpcks. The default is 0.1.
Ngrid : int, optional
Number of grid points. The default is 1000.
ngrid : int, optional
Number of polynomial nodes. The default is 100.
n_order : int, optional
Order of Chebyshev polynomials. The default is 10.
R : function object, optional
Utility (aka Reward) function..
Q : function object, optional
Transition probabilities 𝑄(s_{t},a_{t},s_{t+1}) for the next period state s_{t+1}.
"""
self.functions = functions
self.beta = m_par["beta"] # discount factor
self.m_var = m_var
self.m_par = m_par
self.options = options
self.variables = list(m_var.keys())
self.state_variables = state_variables
self.control_variables = control_variables
self.equations = eqs
self.bellman_equations = be_eqs
self.utilities = utilities
self.value_functions = value_functions
self.lower_bound = lower_boundary
self.upper_bound = upper_boundary
self.Ngrid = Ngrid
self.ngrid = ngrid
self.n = len(m_var) # bunber of variables
self.nc = len(control_variables) # number of control variables.
self.ns = len(state_variables) # number of state variables.
self.n_order = n_order # Order of polinomials
self.n_shocks_states = ns # Number of admissable shocks states
self.rho = rho
self.shock_stddedv = shk_stddedv
self.R = R
self.Q = Q
util_compiled = {}
for u in utilities:
arr = u.split("=")
key = arr[0].strip()
rhs = arr[1].strip()
util_compiled[key] = compile(rhs, "",mode="eval") # Convert string to code object)
self.util_compiled = util_compiled
[docs]
def utility_function(self,m):
"""
Evaluate utility function.
Parameters:
m : dict
Dictionary of variables and parameters names and values.
Returns:
numpy array
Utility values.
"""
arr = []
# for u in self.utilities:
# c = m['c']
# sigma = m["sigma"]
# arr = u.split("=")
# eq = arr[1].strip()
# v = eval(eq,globals(),m)
# arr.append(v.copy())
for key in self.util_compiled:
compiled = self.util_compiled[key]
v = eval(compiled,globals(),m)
arr.append(v.copy())
utility = np.squeeze(np.array(arr))
ind = np.isnan(utility)
utility[ind] = -1.e30
ind = np.isinf(utility)
utility[ind] = -1.e20
return utility
[docs]
def value(self,expr,m):
arr = expr.split("=")
lhs = arr[0].strip()
rhs = arr[1].strip()
v = eval(rhs,globals(),m)
#print(np.min(v),np.max(v))
return lhs,v
[docs]
def updateVariables(self,d,j=None):
"""
Updates control variables.
Parameters:
d : dict
Dictionary of variables and parameters names and values.
j : int
Index of grid.
Returns:
numpy.array
Utility function.
"""
def EXPECTATION(k):
kp = k+"__(1)"
if kp in m:
return m[kp]
elif k in m:
return m[k]
else:
return np.nan
mp = self.m_par.copy()
mp["EXPECTATION"] = EXPECTATION
if j is None:
for v in self.variables:
mp[v] = d[v]
else:
z = np.array([d[x] for x in self.variables])
for i,v in enumerate(self.variables):
mp[v] = z[i][j]
keys = []; m = d.copy()
for eq in self.bellman_equations:
key,val = self.value(eq,mp)
keys.append(key)
m[key] = val
return m,keys
[docs]
def transient_func(self,x,params):
"""
Compute function values.
Parameters:
x : list
Variables values.
params : list
Parameters values.
Returns:
Function values.
"""
f_dynamic = self.functions["f_dynamic"]
f,der = f_dynamic(x,params,order=1)
return f,der
[docs]
def getBounds(self,m):
"""
Returns bound on variables.
Parameters:
m : dict
Locals mapping.
Returns:
Array of 2-tuples.
"""
def f(v):
if isinstance(v,str):
fun = compile(v, "",mode="eval")
x = eval(fun,globals(),m)
return float(x)
else:
return v
var = self.variables
lb = self.lower_bound
if lb is None:
lb = []
ub = self.upper_bound
if ub is None:
ub = []
mp = dict()
for v in var:
if v in lb:
lower = f(lb[v])
else:
lower = -1.e30
if v in ub:
upper = f(ub[v])
else:
upper = 1.e30
mp[v] = (lower,upper)
return mp
[docs]
def value_iteration(self,debug=False):
"""
Solve the OGM by value function iteration.
Parameters:
Instance of tis class.
Returns:
iterations : int
Number of iterations.
crit : float
Convergence value.
y : numpy array
Solution of Bellman equation
"""
y = []
crit = 1.e6 # convergence value
iterations = 0 # number of iterations
dr = np.zeros((self.nc,self.Ngrid),dtype=int) # decision rule
# Initial guess for value function
Tv = np.zeros((self.nc,self.Ngrid))
v = np.zeros((self.nc,self.Ngrid))
# Build control variables grid.
for i in range(self.nc):
var_name = self.control_variables[i]
if var_name in self.options:
kmin,kmax = self.options[var_name]
else:
kmin, kmax = 0, 3*self.m_var[var_name]
# Initialize dictionary with values of variables
m = {}
for k in self.variables:
if k in self.options:
kmin,kmax = self.options[k]
else:
kmin,kmax = -3*self.m_var[k],3*self.m_var[k]
m[k] = np.linspace(kmin,kmax,self.Ngrid)
for k in self.m_par:
m[k] = self.m_par[k]
# Get variables bounds
bounds = self.getBounds(m)
while crit > EPSILON and iterations < NITERATIONS:
for j in range(self.Ngrid):
# Update control variables.
mu,keys = self.updateVariables(m,j)
for i in range(self.nc):
# Get utility
u = self.utility_function(mu)
x = u + self.beta * v[i]
Tv[i,j] = np.max(x)
dr[i,j] = np.argmax(x)
# Update expected control variables by applying the decision rule
mu = m.copy()
for i in range(self.nc):
var = self.control_variables[i]
val = m[var][dr[i]]
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
mu[var+"__(1)"] = val
# Update state variables of Bellman equations
mu,keys = self.updateVariables(mu)
for key in keys:
val = mu[key]
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[key] = val
# Update state variables of transient equations
for eq in self.equations:
#print(eq)
# N = mu["N"]; S = mu["S"]
# print("N",N)
# print("S:",S)
var,val = self.value(eq,mu)
#print(var,val)
if var in self.state_variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
crit = np.max(abs(Tv-v))/np.linalg.norm(Tv)
v = Tv.copy()
iterations += 1
if debug:
print('Iteration # {:03d} \tCriterion: {:.2e}'.format(iterations,crit))
# Update state variables of transient equations
for i in range(2):
for eq in self.equations:
var,val = self.value(eq,m)
if var in self.variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
# Get solution
for var in self.variables:
y.append(m[var])
return iterations,crit,np.array(y)
[docs]
def policy_iteration(self,debug=False):
"""
Solve the OGM by policy function iteration (Howard method).
Parameters:
Instance of tis class.
Returns:
iterations : int
Number of iterations.
crit : float
Convergence value.
y : numpy array
Solution of Bellman equation.
"""
y = []
crit = 1.e6 # convergence value
iterations = 0 # number of iterations
dr = np.zeros((self.nc,self.Ngrid),dtype=int) # decision rule
# Initial guess for value function
Tv = np.zeros((self.nc,self.Ngrid))
v = np.zeros((self.nc,self.Ngrid))
# Build control variables grid.
control_nodes = []; state_nodes = []
for i in range(self.nc):
var_name = self.control_variables[i]
if var_name in self.options:
kmin,kmax = self.options[var_name]
else:
kmin, kmax = 0, 3*self.m_var[var_name]
# Initialize dictionary with values of variables
m = {}
for k in self.variables:
if k in self.options:
kmin,kmax = self.options[k]
else:
kmin,kmax = -3*self.m_var[k],3*self.m_var[k]
m[k] = np.linspace(kmin,kmax,self.Ngrid)
for k in self.m_par:
m[k] = self.m_par[k]
# Get variables bounds
bounds = self.getBounds(m)
while crit > EPSILON and iterations < NITERATIONS:
for j in range(self.Ngrid):
# Update control variables.
mu,keys = self.updateVariables(m,j)
for i in range(self.nc):
# Get utility
u = self.utility_function(mu)
x = u + self.beta * v[i]
Tv[i,j] = np.max(x)
dr[i,j] = np.argmax(x)
# Update expected control variables by applying the decision rule
mu = m.copy()
for i in range(self.nc):
var = self.control_variables[i]
val = m[var][dr[i]]
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
mu[var+"__(1)"] = val
# Update state variables of Bellman equations
mu,keys = self.updateVariables(mu)
for key in keys:
val = mu[key]
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[key] = val
# Update state variables of transient equations
for eq in self.equations:
var,val = self.value(eq,mu)
if var in self.state_variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
Q = sparse.lil_matrix((self.Ngrid*self.nc,self.Ngrid*self.nc))
for i in range(self.nc):
Q0 = sparse.lil_matrix((self.Ngrid,self.Ngrid))
for j in range(self.Ngrid):
Q0[j,dr[i,j]] = 1
i1 = i*self.Ngrid
i2 = (i+1)*self.Ngrid
Q[i1:i2,i1:i2] = Q0
q = Q.todense()
u = self.utility_function(m)
M = sparse.eye(self.Ngrid*self.nc) - self.beta *Q
Tv = spsolve(M, u)
Tv = np.reshape(Tv,(self.nc,self.Ngrid))
crit = np.max(abs(Tv-v))/norm(Tv)
v = Tv.copy()
iterations += 1
if debug:
print('Iteration # {:03d} \tCriterion: {:.2e}'.format(iterations,crit))
# Update state variables of transient equations
for i in range(2):
for eq in self.equations:
var,val = self.value(eq,m)
if var in self.variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
# Get solution
for var in self.variables:
y.append(m[var])
return iterations,crit,np.array(y)
[docs]
def parametric_value_iteration(self,n=None,debug=False):
"""
Solve the OGM by parametric value iteration.
Parameters:
n : int, optional
Number of data points in the grid. The default is 20.
Returns:
iterations : int
Number of iterations.
crit : float
Convergence value.
y : numpy array
Solution of Bellman equation.
"""
from snowdrop.src.numeric.dp.util import Chebychev_Polinomial
y = []
crit = 1.e6 # convergence value
iterations = 0 # number of iterations
ngrid = self.ngrid if n is None else n
# Initial guess for value function
Tv = np.zeros((self.nc,ngrid))
v = np.zeros((self.nc,ngrid))
# Interpolating nodes
rk = -np.cos((2*np.arange(1,ngrid+1)-1)*np.pi/(2.*ngrid))
# Chebyshev polynomials
ch = Chebychev_Polinomial(rk,ngrid)
Cheb = repmat(ch,1,self.nc)
iCheb = pinv(Cheb)
theta = iCheb @ v.T # Initial guess of parameters
# Initialize dictionary with values of variables
m = {}
for k in self.variables:
if k in self.options:
kmin,kmax = self.options[k]
else:
kmin,kmax = -3*self.m_var[k],3*self.m_var[k]
if k in self.control_variables:
m[k] = kmin+(rk+1)*(kmax-kmin)/2 # Mapping
else:
m[k] = np.linspace(kmin,kmax,ngrid)
for k in self.m_par:
m[k] = self.m_par[k]
# Get variables bounds
bounds = self.getBounds(m)
# Value function
def tv(theta):
v = Cheb @ theta
res = []
# Update control variables.
mu,keys = self.updateVariables(m)
for i in range(self.nc):
# Get utility
u = self.utility_function(mu)
value = u + self.beta * v[i]
res.append(value.copy())
res = np.array(res)
return res
# Minus norm of tv function
tv_norm = lambda x: -norm(tv(x))
while crit > EPSILON and iterations < NITERATIONS:
x = fmin(tv_norm,theta,xtol=1.e-3,ftol=1.e-3,maxiter=100,disp=False)
Tv = tv(x)
theta = iCheb @ Tv.T
# Update state variables of Bellman equations
mu,keys = self.updateVariables(m)
for key in keys:
val = mu[key]
if key in bounds:
lower,upper = bounds[key]
val = np.maximum(lower,np.minimum(upper,val))
m[key] = val
# Update state variables of transient equations
for eq in self.equations:
var,val = self.value(eq,mu)
if var in self.state_variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
crit= np.max(abs(Tv-v))/norm(Tv)
v = Tv.copy()
iterations += 1
if debug:
print('Iteration # {:03d} \tCriterion: {:.2e}'.format(iterations,crit))
# Update state variables of transient equations
for i in range(2):
for eq in self.equations:
var,val = self.value(eq,m)
if var in self.variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
# Get solution
for var in self.variables:
y.append(m[var])
return iterations,crit,np.array(y)
[docs]
def stochastic_value_iteration(self,p=0.9,debug=False):
"""
Solve the stochastic OGM by value iteration.
Parameters:
p : float, optional
Probability value. The default is 0.9.
It is used for discrete approximation of VAR(1) stochastic process.
Returns:
iterations : int
Number of iterations.
crit : float
Convergence value.
y : numpy array
Solution of Bellman equation
"""
y = []
crit = 1.e6 # convergence value
iterations = 0 # number of iterations
dr = np.zeros((self.nc,self.Ngrid,self.n_shocks_states),dtype=int) # decision rule
p = (1+self.rho)/2 if p is None else p
PI = np.array([[p,1-p],[1-p,p]])
A = [np.exp(-self.shock_stddedv**2/(1-self.rho**2)),
np.exp(+self.shock_stddedv**2/(1-self.rho**2))]
A = np.array(A)
self.m_par["A"] = A
# Initial guess for value function
Tv = np.zeros((self.nc,self.Ngrid,self.n_shocks_states))
v = np.zeros((self.nc,self.Ngrid,self.n_shocks_states))
# Build control variables grid.
control_nodes = []; state_nodes = []
for i in range(self.nc):
var_name = self.control_variables[i]
if var_name in self.options:
kmin,kmax = self.options[var_name]
else:
kmin, kmax = 0, 3*self.m_var[var_name]
# Initialize dictionary with variables values
m = {}
for k in self.variables:
if k in self.options:
kmin,kmax = self.options[k]
else:
kmin,kmax = -3*self.m_var[k],3*self.m_var[k]
grid = np.linspace(kmin,kmax,self.Ngrid)
m[k] = repmat(grid,self.n_shocks_states,1).T
for k in self.m_par:
m[k] = self.m_par[k]
# Get variables bounds
bounds = self.getBounds(m)
while crit > EPSILON and iterations < NITERATIONS:
for j in range(self.Ngrid):
# Update control variables.
mu,keys = self.updateVariables(m,j)
for i in range(self.nc):
# Get utility
u = self.utility_function(mu)
x = u + self.beta * (v[i] @ PI)
for s in range(self.n_shocks_states):
Tv[i,j,s] = np.max(x[:,s])
dr[i,j,s] = np.argmax(x[:,s])
# Update expected control variables by applying the decision rule
mu = m.copy()
for i in range(self.nc):
var = self.control_variables[i]
arr = []
for s in range(self.n_shocks_states):
q = m[var][:,s]
dri = dr[i,:,s]
val = q[dri]
arr.append(val.copy())
value = np.array(arr)
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
mu[var+"__(1)"] = value.T
# Update state variables of Bellman equations
mu,keys = self.updateVariables(mu)
for key in keys:
m[key] = mu[key]
# Update state variables of transient equations
for eq in self.equations:
var,val = self.value(eq,mu)
if var in self.state_variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
crit = np.max(abs(Tv-v))/norm(Tv)
v = Tv.copy()
iterations += 1
if debug:
print('Iteration # {:03d} \tCriterion: {:.2e}'.format(iterations,crit))
# Update state variables of transient equations
for i in range(2):
for eq in self.equations:
var,val = self.value(eq,m)
if var in self.variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
# Get solution
for var in self.variables:
y.append(m[var])
return iterations,crit,np.array(y)
[docs]
def stochastic_policy_iteration(self,p=0.9,kmin=0.2,kmax=6,debug=False):
"""
Solve the stochastic OGM by policy iteration.
Parameters:
p : float, optional
Probability value. The default is 0.9.
It is used for discrete approximation of VAR(1) stochastic process.
kmin : float, optional
Lower bound on the grid. The default is 0.2
kmax : float, optional
Upper bound on the grid. The default is 6.
Returns:
iterations : int
Number of iterations.
crit : float
Convergence value.
y : numpy array
Solution of Bellman equation
"""
y = []
crit = 1.e6 # convergence value
iterations = 0 # number of iterations
Tv0 = 0
dr = np.zeros((self.nc,self.Ngrid,self.n_shocks_states),dtype=int) # decision rule
p = (1+self.rho)/2 if p is None else p
p=1
PI = np.array([[p,1-p],[1-p,p]])
A = [np.exp(-self.shock_stddedv**2/(1-self.rho**2)),
np.exp(+self.shock_stddedv**2/(1-self.rho**2))]
A = np.array(A)
self.m_par["A"] = A
# Initial guess for value function
v = np.zeros((self.nc,self.Ngrid,self.n_shocks_states))
# Initialize dictionary with values of variables
m = {}
for k in self.variables:
if k in self.options:
kmin,kmax = self.options[k]
else:
kmin,kmax = -3*self.m_var[k],3*self.m_var[k]
grid = np.linspace(kmin,kmax,self.Ngrid)
m[k] = repmat(grid,self.n_shocks_states,1).T
for k in self.m_par:
m[k] = self.m_par[k]
# Get variables bounds
bounds = self.getBounds(m)
while crit > EPSILON and iterations < NITERATIONS:
for j in range(self.Ngrid):
# Update control variables.
mu,keys = self.updateVariables(m,j)
for i in range(self.nc):
# Get utility
u = self.utility_function(mu)
x = u + self.beta * (v[i] @ PI)
for s in range(self.n_shocks_states):
dr[i,j,s] = np.argmax(x[:,s])
# Update expected control variables by applying decision rule
mu = m.copy()
for i in range(self.nc):
var = self.control_variables[i]
arr = []
for s in range(self.n_shocks_states):
q = m[var][:,s]
val = q[dr[i,:,s]]
arr.append(val.copy())
val = np.array(arr)
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
mu[var+"__(1)"] = val.T
# Update state variables of Bellman equations
mu,keys = self.updateVariables(mu)
for key in keys:
m[key] = mu[key]
# Update state variables of transient equations
for eq in self.equations:
var,val = self.value(eq,mu)
if var in self.state_variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
N1 = self.Ngrid*self.n_shocks_states
N = N1*self.nc
Q = sparse.lil_matrix((N,N))
for i in range(self.nc):
Q1 = sparse.lil_matrix((N1,N1))
for s in range(self.n_shocks_states):
Q2 = sparse.lil_matrix((self.Ngrid,self.Ngrid))
for j in range(self.Ngrid):
Q2[j,dr[i,j,s]] = 1
#q2 = Q2.todense()
z = sparse.kron(PI[s],Q2)
Q1[s*self.Ngrid:(s+1)*self.Ngrid] = z
#q1 = Q1.todense()
Q[i*N1:(i+1)*N1] = Q1
#q = Q.todense()
u = self.utility_function(m)
M = sparse.eye(N) - self.beta *Q
Tv = spsolve(M, np.ravel(u.T))
v = np.reshape(Tv,(self.n_shocks_states,self.Ngrid,self.nc)).T
crit= np.max(abs(Tv-Tv0))/norm(Tv)
Tv0 = Tv.copy()
iterations += 1
if debug:
print('Iteration # {:03d} \tCriterion: {:.2e}'.format(iterations,crit))
# Update state variables of transient equations
for i in range(2):
for eq in self.equations:
var,val = self.value(eq,m)
if var in self.variables:
if var in bounds:
lower,upper = bounds[var]
val = np.maximum(lower,np.minimum(upper,val))
m[var] = val
# Get solution
for var in self.variables:
y.append(m[var])
return iterations,crit,np.array(y)
[docs]
def discrete(self):
"""Solve Bellman equation by discrete dynamic programming method."""
import quantecon as qe
ddp = qe.markov.DiscreteDP(self.R, self.Q, self.beta)
results = ddp.solve(method='policy_iteration',epsilon=EPSILON)
y = results.v
crit = results.sigma
iterations = results.num_iter
#self.distr = results.mc.stationary_distributions
return iterations,crit,y
[docs]
def Plot(path_to_dir,data,variable_names,var_labels={}):
from snowdrop.src.graphs.util import plotTimeSeries
header = 'Solution of Bellman Equation'
series = []; labels = []; titles = []
if np.ndim(data) == 3:
ns,n,nvar = data.shape
for i in range(nvar):
ser = []; lbls = []
for j in range(ns):
ser.append(pd.Series(data[j,:,i]))
lbls.append("Markov State " + str(1+j))
series.append(ser)
labels.append(lbls)
var = variable_names[i]
name = var_labels[var] if var in var_labels else var
titles.append(name)
elif np.ndim(data) == 2:
n,nvar = data.shape
for i in range(nvar):
ser = pd.Series(data[:,i])
series.append([ser])
labels.append([])
var = variable_names[i]
name = var_labels[var] if var in var_labels else var
titles.append(name)
else:
nvar = len(y)
for i in range(nvar):
ser = []; lbls = []
data = y[i]
ns = len(data)
for j in range(ns):
ser.append(pd.Series(data[j]))
if ns>1:
lbls.append("Markov State " + str(1+j))
else:
lbls.append(" ")
series.append(ser)
labels.append(lbls)
var = variable_names[i]
name = var_labels[var] if var in var_labels else var
titles.append(name)
if nvar <= 4:
sizes = [2,2]
else:
sizes = [int(np.ceil(nvar/3)),3]
plotTimeSeries(path_to_dir=path_to_dir,header=header,titles=titles,labels=labels,series=series,sizes=sizes)
[docs]
def simulate(model):
"""
Solve Bellman equation.
Parameters:
model : Model object
Instance of model class.
Returns:
numpy.array.
"""
from collections import OrderedDict
t0 = time()
# Get list of control variables equations
eqs = model.symbolic.equations
options = model.options
# Get variables and parameters
var_names = model.symbols["variables"]
var_values = model.calibration["variables"]
var = OrderedDict(zip(var_names,var_values))
par_names = model.symbols["parameters"]
par_values = model.calibration["parameters"]
par = OrderedDict(zip(par_names,par_values))
# Retrieve section related to Bellman equation.
arr = model.symbolic.bellman
assert bool(arr), "Information on Bellman equation is missing in a model file!"
be = arr[0]
# Get Bellman value function
utility_func = be.get("utilities", [])
value_func = be.get("value_functions", [])
be_eqs = be.get("equations", [])
control_variables = be.get("control_variables", [])
lower_boundary = be.get("lower_boundary", None)
upper_boundary = be.get("upper_boundary", None)
# Variables include control and state variables...
# So, get control variables as the subset of variables.
state_variables = [ x for x in var_names if not x in control_variables]
# Instantiate class .
dp = DynProg(functions=model.functions,m_var=var,m_par=par,options=options,state_variables=state_variables,
control_variables=control_variables,utilities=utility_func,value_functions=value_func,
eqs=eqs,be_eqs=be_eqs,lower_boundary=lower_boundary,upper_boundary=upper_boundary)
# Solve Bellman equation
iterations,crit,y = dp.value_iteration()
# iterations,crit,y = dp.policy_iteration()
# iterations,crit,y = dp.parametric_value_iteration()
# iterations,crit,y = dp.stochastic_value_iteration()
# iterations,crit,y = dp.stochastic_policy_iteration()
elapsed = time() - t0
return iterations,y.T,crit,elapsed
if __name__ == '__main__':
"""The main entry point."""
from snowdrop.src.driver import importModel
fname = 'DP/ff.yaml' # Bellman equation example
fpath = os.path.dirname(os.path.abspath(__file__))
path_to_dir = os.path.abspath(os.path.join(fpath,'../../../graphs'))
file_path = os.path.abspath(os.path.join(fpath, '../../../models', fname))
## Create model object
model = importModel(file_path)
## Run simulations
iterations, y, crit, elapsed = simulate(model=model)
# Plot Evolution of Endogenous Variables
variable_names = model.symbols["variables"]
var_labels = model.symbols.get("variables_labels",{})
Plot(path_to_dir=path_to_dir,data=y,variable_names=variable_names,var_labels=var_labels)