def value_of_choice(self, c, t, m): """ value of choice of c used in vfi """ par = self.par sol = self.sol # a. end-of-period assets a = m - c # b. next-period cash-on-hand still_working_next_period = t + 1 <= par.TR - 1 if still_working_next_period: fac = par.G * par.L[t] * par.psi_vec w = par.w xi = par.xi_vec else: fac = par.G * par.L[t] w = 1 xi = 1 m_plus = (par.R / fac) * a + xi # c. continuation value inv_v_plus = np.zeros(m_plus.size) linear_interp.interp_1d_vec(sol.m[t + 1, :], sol.inv_v[t + 1, :], m_plus, inv_v_plus) v_plus = 1 / inv_v_plus # d. value-of-choice total = utility( c, par) + par.beta * np.sum(w * fac**(1 - par.rho) * v_plus) return -total
def solve_backwards(par, r, w, Va_p, Va, a, c, m, V_p, V, Vbar): """ perform time iteration step with Va_p from previous iteration """ # a. post-decision marg_u_plus = (par.beta * par.e_trans) @ Va_p Vbar[:, :] = (par.beta * par.e_trans) @ V_p # b. egm loop for i_e in prange(par.Ne): # i. egm c_endo = marg_u_plus[i_e]**(-1 / par.sigma) m_endo = c_endo + par.a_grid # ii. interpolation linear_interp.interp_1d_vec(m_endo, par.a_grid, m[i_e], a[i_e]) a[i_e, 0] = np.fmax(a[i_e, 0], 0) c[i_e] = m[i_e] - a[i_e] # iii. envelope condition Va[i_e] = (1 + r) * c[i_e]**(-par.sigma) # iv. value function for i_a in range(par.Na): a[i_e, i_a] = np.fmax(a[i_e, i_a], 0) Vbar_now = linear_interp.interp_1d(par.a_grid, Vbar[i_e], a[i_e, i_a]) V[i_e, i_a] = c[i_e, i_a]**(1 - par.sigma) / (1 - par.sigma) + Vbar_now
def solve(sol,par,G2EGM=True): # a. last_period t = par.T-1 sol.m_ret[t,:] = par.grid_m_ret sol.c_ret[t,:] = sol.m_ret[t,:] v = utility.func_ret(sol.c_ret[t,:],par) sol.inv_v_ret[t,:] = -1.0/v vm = utility.marg_func(sol.c_ret[t,:],par) sol.inv_vm_ret[t,:] = 1.0/vm if G2EGM: sol.inv_vn_ret[t,:] = sol.inv_vm_ret[t,:] # b. backwards inducation for j in range(2,par.T+1): t = par.T-j # i. optimal c choice m_plus = par.Ra*par.grid_a_ret + par.yret c_plus = np.zeros(m_plus.shape) linear_interp.interp_1d_vec(sol.m_ret[t+1,:],sol.c_ret[t+1,:],m_plus,c_plus) vm_plus = utility.marg_func(c_plus,par) q = par.beta*par.Ra*vm_plus sol.c_ret[t,par.Nmcon_ret:] = utility.inv_marg_func(q,par) # ii. constraint sol.c_ret[t,:par.Nmcon_ret] = nonlinspace_jit(1e-6,sol.c_ret[t,par.Nmcon_ret]*0.999,par.Nmcon_ret,par.phi_m) # iii. end-of-period assets and value-of-choice sol.a_ret[t,par.Nmcon_ret:] = par.grid_a_ret inv_v_plus = np.zeros(m_plus.shape) linear_interp.interp_1d_vec(sol.m_ret[t+1,:],sol.inv_v_ret[t+1,:],m_plus,inv_v_plus) v_plus = -1.0/inv_v_plus v1 = utility.func_ret(sol.c_ret[t,:par.Nmcon_ret],par) + par.beta*v_plus[0] v2 = utility.func_ret(sol.c_ret[t,par.Nmcon_ret:],par) + par.beta*v_plus sol.inv_v_ret[t,:par.Nmcon_ret] = -1.0/v1 sol.inv_v_ret[t,par.Nmcon_ret:] = -1.0/v2 # iv. endogenous grid sol.m_ret[t,:] = sol.a_ret[t,:] + sol.c_ret[t,:] # v. marginal v vm = utility.marg_func(sol.c_ret[t,:],par) sol.inv_vm_ret[t,:] = 1.0/vm if G2EGM: sol.inv_vn_ret[t,:] = sol.inv_vm_ret[t,:]
def EGMvec(self, t, m, c, inv_v): """ EGM with fully vectorized code """ par = self.par sol = self.sol # a. prep if t + 1 <= par.TR - 1: # still working in next-period a = par.grid_a_tile[t, :] fac = par.G * par.L[t] * par.psi_vec_rep w = par.w_rep xi = par.xi_vec_rep Nshocks = par.Nshocks else: a = par.grid_a fac = par.G * par.L[t] w = 1 xi = 1 Nshocks = par.Nshocks inv_fac = 1.0 / fac # b. future m and c m_plus = inv_fac * par.R * a + xi c_plus = np.zeros(m_plus.size) linear_interp.interp_1d_vec(sol.m[t + 1, :], sol.c[t + 1, :], m_plus, c_plus) inv_v_plus = np.zeros(m_plus.size) linear_interp.interp_1d_vec(sol.m[t + 1, :], sol.inv_v[t + 1, :], m_plus, inv_v_plus) v_plus = 1.0 / inv_v_plus # c. average future marginal utility marg_u_plus = self.marg_utility(fac * c_plus) avg_marg_u_plus = np.sum((w * marg_u_plus).reshape((Nshocks, par.Na)), axis=0) avg_v_plus = np.sum((w * (fac**(1 - par.rho)) * v_plus).reshape( (Nshocks, par.Na)), axis=0) # d. current c c[:] = self.inv_marg_utility(par.beta * par.R * avg_marg_u_plus) # e. current m m[:] = par.grid_a[t, :] + c # f. current v I = c > 0 inv_v[I] = 1.0 / (self.utility(c[I]) + par.beta * avg_v_plus[I]) inv_v[~I] = 0.0
def EGM(self, t, m, c, inv_v): """ EGM with partly vectorized code """ par = self.par sol = self.sol # loop over end-of-period assets for i_a in range(par.Na): # a. prep a = par.grid_a[t, i_a] if t + 1 <= par.TR - 1: # still working in next-period fac = par.G * par.L[t] * par.psi_vec w = par.w xi = par.xi_vec else: fac = par.G * par.L[t] w = 1 xi = 1 inv_fac = 1.0 / fac # b. future m and c (vectors) m_plus = inv_fac * par.R * a + xi c_plus = np.zeros(m_plus.size) linear_interp.interp_1d_vec(sol.m[t + 1, :], sol.c[t + 1, :], m_plus, c_plus) inv_v_plus = np.zeros(m_plus.size) linear_interp.interp_1d_vec(sol.m[t + 1, :], sol.inv_v[t + 1, :], m_plus, inv_v_plus) v_plus = 1.0 / inv_v_plus # c. average future marginal utility (number) marg_u_plus = self.marg_utility(fac * c_plus) avg_marg_u_plus = np.sum(w * marg_u_plus) avg_v_plus = np.sum(w * (fac**(1 - par.rho)) * v_plus) # d. current c c[i_a] = self.inv_marg_utility(par.beta * par.R * avg_marg_u_plus) # e. current m m[i_a] = a + c[i_a] # f. current v if c[i_a] > 0: inv_v[i_a] = 1.0 / (self.utility(c[i_a]) + par.beta * avg_v_plus) else: inv_v[i_a] = 0
def _discrete(model, t, i_p): par = model.par # a. interpolation n, m = np.meshgrid(par.grid_n, par.grid_m, indexing='ij') x = m + (1 - par.tau) * n inv_v_adj = np.zeros(x.size) linear_interp.interp_1d_vec(par.grid_x, model.sol.inv_v_adj[t, i_p, :, ], x.ravel(), inv_v_adj) inv_v_adj = inv_v_adj.reshape(x.shape) # f. best discrete choice fig = plt.figure(figsize=(6, 6)) ax = fig.add_subplot(1, 1, 1) I = inv_v_adj > model.sol.inv_v_keep[t, i_p, :, :] x = m[I].ravel() y = n[I].ravel() ax.scatter(x, y, s=2, label='adjust') x = m[~I].ravel() y = n[~I].ravel() ax.scatter(x, y, s=2, label='keep') ax.set_title( f'optimal discrete choice ($t = {t}$, $p = {par.grid_p[i_p]:.2f}$)', pad=10) legend = ax.legend(loc='upper center', shadow=True) frame = legend.get_frame() frame.set_facecolor('0.90') # g. details ax.grid(True) ax.set_xlabel('$m_t$') ax.set_xlim([par.grid_m[0], par.grid_m[-1]]) ax.set_ylabel('$n_t$') ax.set_ylim([par.grid_n[0], par.grid_n[-1]]) plt.show()
def time_iteration(par, r, w, Va_p, Va, a, c, m): """ perform time iteration step with Va_p from previous iteration """ # a. post-decision marg_u_plus = (par.beta * par.e_trans) @ Va_p # b. egm loop for i_e in prange(par.Ne): # i. egm c_endo = marg_u_plus[i_e]**(-1 / par.sigma) m_endo = c_endo + par.a_grid # ii. interpolation linear_interp.interp_1d_vec(m_endo, par.a_grid, m[i_e], a[i_e]) a[i_e, 0] = np.fmax(a[i_e, 0], 0) c[i_e] = m[i_e] - a[i_e] # iii. envelope condition Va[i_e] = (1 + r) * c[i_e]**(-par.sigma)
def solve_backwards(par,r,w,Va_p,Va,a,c,m): """ solve backwards with Va_p from previous iteration """ # a. post-decision marg_u_plus = (par.beta*par.e_trans)@Va_p # b. egm loop for i_e in prange(par.Ne): # i. egm c_endo = marg_u_plus[i_e]**(-1/par.sigma) m_endo = c_endo + par.a_grid # ii. interpolation linear_interp.interp_1d_vec(m_endo,par.a_grid,m[i_e],a[i_e]) a[i_e,0] = np.fmax(a[i_e,0],0) # enforce borrowing constraint c[i_e] = m[i_e]-a[i_e] # iii. envelope condition Va[i_e] = (1+r)*c[i_e]**(-par.sigma)
def simulate_timeloop(self): """ simulate model with loop over time """ par = self.par sol = self.sol sim = self.sim # loop over time for t in range(par.simT): # a. solution if par.simlifecycle == 0: grid_m = sol.m[0, :] grid_c = sol.c[0, :] else: grid_m = sol.m[t, :] grid_c = sol.c[t, :] # b. consumption linear_interp.interp_1d_vec(grid_m, grid_c, sim.m[:, t], sim.c[:, t]) sim.a[:, t] = sim.m[:, t] - sim.c[:, t] # c. next-period states if t < par.simT - 1: if t + 1 > par.TR - 1: sim.m[:, t + 1] = par.R * sim.a[:, t] / (par.G * par.L[t]) + 1 sim.p[:, t + 1] = np.log(par.G) + np.log(par.L[t]) + sim.p[:, t] sim.y[:, t + 1] = sim.p[:, t + 1] else: sim.m[:, t + 1] = par.R * sim.a[:, t] / ( par.G * par.L[t] * sim.psi[:, t + 1]) + sim.xi[:, t + 1] sim.p[:, t + 1] = np.log(par.G) + np.log( par.L[t]) + sim.p[:, t] + np.log(sim.psi[:, t + 1]) I = sim.xi[:, t + 1] > 0 sim.y[I, t + 1] = sim.p[I, t + 1] + np.log(sim.xi[I, t + 1])
def plot_buffer_stock_target(model): par = model.par sol = model.sol # a. find a and avg. m_plus and c_plus # allocate a = np.nan * np.ones(par.Na + 1) m_plus = np.nan * np.ones(par.Na + 1) C_plus = np.nan * np.ones(par.Na + 1) delta_log_C_plus = np.nan * np.ones(par.Na + 1) delta_log_C_plus_approx_2 = np.nan * np.ones(par.Na + 1) fac = 1.0 / (par.G * par.psi_vec) for i_a in range(par.Na + 1): # a. a and m a[i_a] = sol.m[0, i_a] - sol.c[0, i_a] m_plus[i_a] = np.sum(par.w * (fac * par.R * a[i_a] + par.xi_vec)) # b. C_plus m_plus_vec = fac * par.R * a[i_a] + par.xi_vec c_plus_vec = np.zeros(m_plus_vec.size) linear_interp.interp_1d_vec(sol.m[0, :], sol.c[0, :], m_plus_vec, c_plus_vec) C_plus_vec = par.G * par.psi_vec * c_plus_vec C_plus[i_a] = np.sum(par.w * C_plus_vec) # c. approx if not (par.sigma_xi == 0 and par.sigma_psi == 0 and par.pi == 0) and sol.c[0, i_a] > 0: delta_log_C_plus[i_a] = np.sum( par.w * (np.log(par.G * C_plus_vec))) - np.log(sol.c[0, i_a]) var_C_plus = np.sum( par.w * (np.log(par.G * C_plus_vec) - np.log(sol.c[0, i_a]) - delta_log_C_plus[i_a])**2) delta_log_C_plus_approx_2[i_a] = par.rho**(-1) * (np.log( par.R * par.beta)) + 2 / par.rho * var_C_plus + np.log(par.G) # b. find target i = np.argmin(np.abs(m_plus - sol.m[0, :])) m_target = sol.m[0, i] # c. figure 1 - buffer-stock target fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(1, 1, 1) # limits ax.set_xlim([np.min(par.a_min), 5]) ax.set_ylim([0, 5]) # layout bbox = {'boxstyle': 'square', 'ec': 'white', 'fc': 'white'} ax.text(2.1, 0.25, f'$\\beta = {par.beta:.2f}$, $R = {par.R:.2f}$, $G = {par.G:.2f}$', bbox=bbox) ax.set_xlabel('$m_t$') ax.set_ylabel('') # i. consumption ax.plot(sol.m[0, :], sol.c[0, :], '-', lw=1.5, label='$c(m_t)$') ax.legend(loc='upper left', frameon=True) fig.savefig(f'figs/buffer_stock_target_{model.name}_c.pdf') # ii. perfect foresight solution if par.FHW < 1 and par.RI < 1: c_pf = (1 - par.RI) * (sol.m[0, :] + (1 - par.FHW)**(-1) - 1) ax.plot(sol.m[0, :], c_pf, ':', lw=1.5, color='black', label='$c^{PF}(m_t)$') ax.legend(loc='upper left', frameon=True) fig.savefig(f'figs/buffer_stock_target_{model.name}_pf.pdf') # iii. a ax.plot(sol.m[0, :], a, '-', lw=1.5, label=r'$a_t=m_t-c^{\star}(m_t)$') ax.legend(loc='upper left', frameon=True) fig.savefig(f'figs/buffer_stock_target_{model.name}_a.pdf') # iv. m_plus ax.plot(sol.m[0, :], m_plus, '-', lw=1.5, label='$E[m_{t+1} | a_t]$') ax.legend(loc='upper left', frameon=True) fig.savefig(f'figs/buffer_stock_target_{model.name}_m_plus.pdf') # v. 45 ax.plot([0, 5], [0, 5], '-', lw=1.5, color='black', label='45 degree') ax.legend(loc='upper left', frameon=True) fig.savefig(f'figs/buffer_stock_target_{model.name}_45.pdf') # vi. target if not (par.sigma_xi == 0 and par.sigma_psi == 0 and par.pi == 0) == 'bs' and par.GI < 1: ax.plot([m_target, m_target], [0, 5], '--', lw=1.5, color='black', label=f'target = {m_target:.2f}') ax.legend(loc='upper left', frameon=True) fig.savefig(f'figs/buffer_stock_target_{model.name}.pdf') # STOP if par.sigma_xi == 0 and par.sigma_psi == 0 and par.pi == 0: return # d. figure 2 - C ratio fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(1, 1, 1) I = sol.c[0, :] > 0 ax.plot(sol.m[0, I], (C_plus[I] / sol.c[0, I]), '-', lw=1.5, label='$E[C_{t+1}/C_t]$') ax.plot([m_target, m_target], [0, 10], '--', lw=1.5, color='black', label='target') ax.plot([np.min(par.a_min), 500], [par.G, par.G], ':', lw=1.5, color='black', label='$G$') ax.plot([np.min(par.a_min), 500], [(par.R * par.beta)**(1 / par.rho), (par.R * par.beta)**(1 / par.rho)], '-', lw=1.5, color='black', label=r'$(\beta R)^{1/\rho}$') # limit ax.set_xlim([np.min(par.a_min), 10]) ax.set_ylim([0.95, 1.1]) # layout ax.set_xlabel('$m_t$') ax.set_ylabel('$C_{t+1}/C_t$') ax.legend(loc='upper right', frameon=True) fig.savefig(f'figs/cons_growth_{model.name}.pdf') # e. figure 3 - euler approx fig = plt.figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(1, 1, 1) ax.plot(sol.m[0, :], delta_log_C_plus, '-', lw=1.5, label=r'$E[\Delta \log C_{t+1}]$') ax.plot(sol.m[0, :], par.rho**(-1) * np.log(par.R * par.beta) * np.ones(par.Na + 1) + np.log(par.G), '-', lw=1.5, label='1st order approx.') ax.plot(sol.m[0, :], delta_log_C_plus_approx_2, '-', lw=1.5, label='2nd order approx.') ax.plot([m_target, m_target], [-10, 10], '--', lw=1.5, color='black', label='target') # limit ax.set_xlim([np.min(par.a_min), 10]) ax.set_ylim([-0.03, 0.12]) # layout ax.set_xlabel('$m_t$') ax.set_ylabel(r'$E[\Delta \log C_{t+1}]$') ax.legend(loc='upper right', frameon=True) fig.savefig(f'figs/euler_approx_{model.name}.pdf')
def matrix(l, n, v_a): #parameter Na = 1000 Ne = 11 # number of states sol_shape = (Ne, Na) a = np.zeros(sol_shape) r = 0.03 w = 1.000 a_grid = equilogspace(0, 200, Na) rho = 0.97 # AR(1) parameter sigma_e = 0.25 # std. of persistent shock sigma = 2 beta = 0.96 e_grid, e_trans, e_ergodic, e_trans_cumsum, e_ergodic_cumsum = markov_rouwenhorst( rho, sigma_e, Ne) #calculation #step 3 A = np.kron(e_trans, np.identity(n)) B = (beta * A) @ v_a C = np.power(B, (-sigma)) a_extend = np.tile(a_grid, 11) m_endo = C + a_extend #samme opbygning som vektoren v_a #step 4 values = [] m = (1 + r) * a_grid[np.newaxis, :] + w * e_grid[:, np.newaxis] new_m_endo = np.array_split(m_endo, Ne) for k in range(Ne): linear_interp.interp_1d_vec(new_m_endo[k], a_grid, m[k], a[k]) for i_a in range(Na): a[k, i_a] = np.fmax(a[k, i_a], 0) values.append(m[k] - a[k]) #print(f'new_m_endo[0]:{new_m_endo[0]}') #print(new_m_endo[0]) c = np.concatenate(values) u = np.power(c, (1 - sigma)) / (1 - sigma) u[u == -np.inf] = -1000000000000000000000000000 print(f'c:{c}') print(f'u:{u}') #Create Q^ lis = [] Q = np.zeros(Ne * Na) #calculate for e in range(Ne): q = np.zeros((Na, Na)) #create each Q_i for k in range(Na): opt = a[e, k] if opt >= np.max(a_grid): q[k, Na - 1] = 1 #If opt equals the end point elif opt <= np.min(a_grid): q[k, 0] = 1 #If opt equals the start point else: a_high = np.min(np.nonzero(opt <= a_grid)) #create points a_low = np.max(np.nonzero(opt >= a_grid)) q[k, a_low] = (a_grid[a_low + 1] - opt) / (a_grid[a_low + 1] - a_grid[a_low]) q[k, a_high] = (opt - a_grid[a_high - 1]) / (a_grid[a_high] - a_grid[a_high - 1]) lis.append(q) Q = block_diag(*lis) #make block matrix from Q's omega = Q @ A new_v_a = np.linalg.inv(np.identity(11000) - beta * omega) @ u return new_v_a