def EGM_loop(sol, t, par): for i_a, a in enumerate(par.grid_a[t, :]): if t + 1 <= par.Tr: # No pension in the next period fac = par.G * par.L[t] * par.psi_vec w = par.w xi = par.xi_vec inv_fac = 1 / fac # Futute m and c m_plus = inv_fac * par.R * a + xi c_plus = tools.interp_linear_1d(sol.m[t + 1, :], sol.c[t + 1, :], m_plus) else: fac = par.G * par.L[t] w = 1 xi = 1 inv_fac = 1 / fac # Futute m and c m_plus = inv_fac * par.R * a + xi c_plus = tools.interp_linear_1d_scalar(sol.m[t + 1, :], sol.c[t + 1, :], m_plus) # Future marginal utility marg_u_plus = marg_util(fac * c_plus, par) avg_marg_u_plus = np.sum(w * marg_u_plus) # Currect C and m sol.c[t, i_a + 1] = inv_marg_util(par.beta * par.R * avg_marg_u_plus, par) sol.m[t, i_a + 1] = a + sol.c[t, i_a + 1] return sol
def value_of_choice(x,m,M_next,t,V1_next,V2_next,V_pens,par,N,p): #"unpack" c1 if type(x) == np.ndarray: # vector-type: depends on the type of solver used c1 = x[0] c2 = x[1] else: c = x # define assets a = m - c1 - c2 EV_next = 0.0 #Initialize if t+1<= par.Tr: # No pension in the next period for i,psi in enumerate(par.psi_vec): fac = par.G*psi w = par.w[i] xi = par.xi_vec[i] inv_fac = 1/fac # Future m and c M_plus = inv_fac*par.R*a+xi V1_plus = tools.interp_linear_1d_scalar(M_next,V1_next,M_plus) V2_plus = tools.interp_linear_1d_scalar(M_next,V2_next,M_plus) EV_next += p[N,t]*w*V1_plus + (1-p[N,t])*w*V2_plus else: N = 0 fac = par.G_ret w = 1 nu = par.nu inv_fac = 1/fac # Future m and c M_plus = inv_fac*par.R*a+nu V_plus = tools.interp_linear_1d_scalar(M_next,V_pens,M_plus) EV_next += w*V_plus # Value of choice V_guess = util(c1,c2,par,N)+par.beta*EV_next return V_guess
def value_of_choice(x, m, M_next, t, V1_next, V2_next, N, par): #"unpack" c1 if type(x ) == np.ndarray: # vector-type: depends on the type of solver used c1 = x[0] c2 = x[1] else: c = x a = m - c1 - c2 p = child_prob(par) EV_next = 0.0 #Initialize if t + 1 <= par.Tr: # No pension in the next period for i in range(0, len(par.w)): fac = par.G * par.psi_vec[i] w = par.w[i] xi = par.xi_vec[i] inv_fac = 1 / fac # Future m and c M_plus = inv_fac * par.R * a + par.xi_vec[i] V1_plus = tools.interp_linear_1d_scalar(M_next, V1_next, M_plus) V2_plus = tools.interp_linear_1d_scalar(M_next, V2_next, M_plus) EV_next += p[N, t] * w * V1_plus + (1 - p[N, t]) * w * V2_plus else: fac = par.G w = 1 xi = 1 inv_fac = 1 / fac # Future m and c M_plus = inv_fac * par.R * a + xi V_plus = tools.interp_linear_1d_scalar(M_next, V2_next, M_plus) EV_next += w * V_plus # Value of choice V_guess = util(c1, c2, N, par) + par.beta * EV_next return V_guess
def value_of_choice_2d(x, a, a_next, v_next, par, state): # Unpack consumption (choice variable) c = x # Intialize expected continuation value Ev_next = 0.0 # Compute value of choice conditional on being in state 1 (unemployment state) ###### VECTORIZE THIS if state == 1: # Loop over each possible state for i in [0, 1]: # Next periods state for each income level a_plus = par.y[i] + (1 + par.r) * (a - c) #Interpolate continuation given state a_plus v_plus = tools.interp_linear_1d_scalar(a_next, v_next, a_plus) # Append continuation value to calculate expected value Ev_next += par.P[0, i] * v_plus # Compute value of choice conditional on being in state 2 (employment state) else: # Loop over each possible state ###### VECTORIZE THIS for i in [0, 1]: # Next periods state for each income level a_plus = par.y[i] + (1 + par.r) * (a - c) #Interpolate continuation given state a_plus v_plus = tools.interp_linear_1d_scalar(a_next, v_next, a_plus) # Append continuation value to calculate expected value Ev_next += par.P[1, i] * v_plus # Value of choice v_guess = util.u(c, par) + par.beta * Ev_next return v_guess
def EGM (sol,t,par): for i_a,a in enumerate(par.grid_a[t,:]): if t+1<= par.Tr: # No pension in the next period fac = par.G*par.psi_vec w = par.w xi = par.xi_vec inv_fac = 1/fac # Future m and c m_plus = inv_fac*par.R*a+xi c1_plus = tools.interp_linear_1d(sol.m[t+1,:],sol.c1[t+1,:], m_plus) c2_plus = tools.interp_linear_1d(sol.m[t+1,:],sol.c2[t+1,:], m_plus) else: fac = par.G w = 1 xi = 1 inv_fac = 1/fac # Future m and c m_plus = inv_fac*par.R*a+xi c1_plus = tools.interp_linear_1d_scalar(sol.m[t+1,:],sol.c1[t+1,:], m_plus) c2_plus = tools.interp_linear_1d_scalar(sol.m[t+1,:],sol.c2[t+1,:], m_plus) # Future marginal utility marg_u_plus1 = marg_util_c1(fac*c1_plus,par) marg_u_plus2 = marg_util_c2(fac*c2_plus,par) avg_marg_u_plus1 = np.sum(w*marg_u_plus1) avg_marg_u_plus2 = np.sum(w*marg_u_plus2) # Current C and m sol.c1[t,i_a+1]=inv_marg_util(par.beta*par.R*avg_marg_u_plus1,par) sol.c2[t,i_a+1]=inv_marg_util(par.beta*par.R*avg_marg_u_plus2,par) sol.m[t,i_a+1]=a+sol.c1[t,i_a+1]+sol.c2[t,i_a+1] return sol
def obj_keep( arg, n, m, v_next, par, m_next ): # I have added m_next to the interpolation since it changes throughout iterations # Unpack c = arg # End of period assets m_plus = (1 + par.r) * (m - c) + par.y1 # Continuation value v_plus = tools.interp_linear_1d_scalar(m_next, v_next, m_plus) # Value of choice value = util.u_h(c, n, par) + par.beta * v_plus return value
def obj_keep(arg, n, m, v_next, par): # Unpack c = arg # End of period assets m_plus = (1 + par.r) * (m - c) + par.y1 # Continuation value v_plus = tools.interp_linear_1d_scalar(par.grid_m, v_next, m_plus) # Value of choice value = util.u_h(c, n, par) + par.beta * v_plus return value #def solve_NEGM(par,sol): # a. next-period cash-on-hand m_plus = par['R'] * par['grid_a'] + par['y'] # b. post-decision value function sol['w_vec'] = np.empty(m_plus.size) linear_interp.interp_1d_vec(par['grid_m'], sol['v_next'], m_plus, sol['w_vec']) # c. post-decision marginal value of cash c_next_interp = np.empty(m_plus.size) linear_interp.interp_1d_vec(par['grid_m'], sol['c_next'], m_plus, c_next_interp) q = par['beta'] * par['R'] * marg_u(c_next_interp, par) # d. EGM sol['c_vec'] = inv_marg_u(q, par) sol['m_vec'] = par['grid_a'] + sol['c_vec'] myupperenvelope = upperenvelope.create(u) # where is the utility function # b. apply upperenvelope c_ast_vec = np.empty(par['grid_m'].size) # output v_ast_vec = np.empty(par['grid_m'].size) # output myupperenvelope(par['grid_a'], sol['m_vec'], sol['c_vec'], sol['w_vec'], par['grid_m'], c_ast_vec, v_ast_vec, par['rho']) return #sol from upperenvelope
def value_of_choice(x, a, a_next, v_next, par): # Unpack consumption (choice variable) c = x # Intialize expected continuation value Ev_next = 0.0 # Loop over each possible state for i in [0, 1]: # Next periods state for each income level a_plus = par.y[i] + (1 + par.r) * (a - c) #Interpolate continuation given state a_plus v_plus = tools.interp_linear_1d_scalar(a_next, v_next, a_plus) # Append continuation value to calculate expected value Ev_next += par.Pi[i] * v_plus # Value of choice v_guess = util.u(c, par) + par.beta * Ev_next return v_guess
def value_functions(par, sol, sim): # Compute value function for z in [0, 1]: for i_h in prange(par.Nh): for i, c in enumerate(par.grid_m): sol.inv_v[-1, i, z, i_h] = 1.0 / utility(c, par.rho) # Before last period for t in range(par.T - 2, -1, -1): for i_h in range(par.Nh): #prange h = par.grid_h[i_h] if t >= par.TR and i_h != 0: # After retirement, solution is independent of z and h sol.inv_v[t, :, z, i_h] = sol.inv_v[t, :, 0, 0] elif t >= par.TH and z == 1 and i_h != 0: # After early holiday pay, solution is independent of h sol.inv_v[t, :, z, i_h] = sol.inv_v[t, :, z, 0] elif t < par.TH - 1 and i_h != 0: # Before holiday pay decision, solution is indenpendent of z and h sol.inv_v[t, :, z, i_h] = sol.inv_v[t, :, 0, 0] else: for i_m in prange(par.Nm): m = par.grid_m[i_m] c = tools.interp_linear_1d_scalar( sol.m[t, :, z, i_h], sol.c[t, :, z, i_h], m) v = value_of_choice(c, t, m, h, z, par, sol) sol.inv_v[t, i_m, z, i_h] = -1.0 / v
def solve_dc(sol, par, v_next, c_next, h_next, m_next): # a. Solve the keeper problem shape = ( 2, np.size(par.grid_a) ) # Row for each state of housing and colums for exogenous end-of-period assets grid # Intialize v_keep = np.zeros(shape) + np.nan c_keep = np.zeros(shape) + np.nan h_keep = np.zeros(shape) + np.nan # Loop over housing states for n in range(2): # Loop over exogenous states (post decision states) for a_i, a in enumerate(par.grid_a): #Next periods assets and consumption m_plus = (1 + par.r) * a + par.y1 # Interpolate next periods consumption c_plus = tools.interp_linear_1d_scalar(m_next[n, :], c_next[n, :], m_plus) # Marginal utility marg_u_plus = util.marg_u(c_plus, par) #av_marg_u_plus = np.sum(par.P*marg_u_plus, axis = 1) # Dot product by row (axis = 1) #### no average # Add optimal consumption and endogenous state using Euler equation c_keep[n, a_i] = util.inv_marg_u( (1 + par.r) * par.beta * marg_u_plus, par) #### no average # v_keep[n,a_i] = obj_keep(c_keep[n,a_i], n, c_keep[n,a_i] + a, v_next[n,:], par, m_next[n, :]) # The line below is faster and more precise as it avoids numerical errors v_keep[n, a_i] = util.u_h( c_keep[n, a_i], n, par) + par.beta * tools.interp_linear_1d_scalar( m_next[n, :], v_next[n, :], m_plus) h_keep[n, a_i] = n ### UPPER ENVELOPE ### c_keep, v_keep, m_grid = upper_envelope(c_keep, v_keep, v_next, m_next, shape, par) ### Add points at the constraints ### m_con = np.array([ np.linspace(0 + 1e-8, m_grid[0, 0] - 1e-4, par.N_bottom), np.linspace(0 + 1e-8, m_grid[1, 0] - 1e-4, par.N_bottom) ]) c_con = m_con.copy() v_con_0 = [ obj_keep(c_con[0, i], 0, m_con[0, i], v_next[0, :], par, m_next[0, :]) for i in range(par.N_bottom) ] # From N_bottom or whole v_con_1 = [ obj_keep(c_con[1, i], 1, m_con[1, i], v_next[1, :], par, m_next[1, :]) for i in range(par.N_bottom) ] # From N_bottom or whole v_con = np.array([v_con_0, v_con_1]) # initialize new larger keeper containers new_shape = (2, np.size(par.grid_a) + par.N_bottom) c_keep_append = np.zeros(new_shape) + np.nan v_keep_append = np.zeros(new_shape) + np.nan m_grid_append = np.zeros(new_shape) + np.nan # append for i in range(2): c_keep_append[i, :] = np.append(c_con[i, :], c_keep[i, :]) v_keep_append[i, :] = np.append(v_con[i, :], v_keep[i, :]) m_grid_append[i, :] = np.append(m_con[i, :], m_grid[i, :]) # b. Solve the adjuster problem # Initialize v_adj = np.zeros(new_shape) + np.nan c_adj = np.zeros(new_shape) + np.nan h_adj = np.zeros(new_shape) + np.nan # Loop over housing state for n in range(2): # Housing choice is reverse of state n if adjusting h = 1 - n # Loop over asset grid for a_i, m in enumerate(m_grid_append[n]): # endogenous grid # If adjustment is not possible if n == 0 and m < par.ph: v_adj[n, a_i] = -np.inf c_adj[n, a_i] = 0 h_adj[n, a_i] = np.nan else: # Assets available after adjusting if n == 1: p = par.p1 else: p = par.ph x = m - p * (h - n) # Value of choice v_adj[n, a_i] = tools.interp_linear_1d_scalar( m_grid_append[h], v_keep_append[h, :], x) c_adj[n, a_i] = tools.interp_linear_1d_scalar( m_grid_append[h], c_keep_append[h, :], x) h_adj[n, a_i] = h # c. Combine solutions # Loop over asset grid again for n in range(2): for a_i, m in enumerate(m_grid_append[n]): # endogenous grid # If keeping is optimal if v_keep_append[n, a_i] > v_adj[n, a_i]: sol.v[n, a_i] = v_keep_append[n, a_i] sol.c[n, a_i] = c_keep_append[n, a_i] sol.h[n, a_i] = n sol.m[n, a_i] = m_grid_append[n, a_i] # added # If adjusting is optimal else: sol.v[n, a_i] = v_adj[n, a_i] sol.c[n, a_i] = c_adj[n, a_i] sol.h[n, a_i] = 1 - n sol.m[n, a_i] = m_grid_append[n, a_i] # added for i in range(2): sol.delta_save[i, sol.it] = max(abs(sol.v[i] - v_next[i])) return sol
def solve_NEGM(sol, par, v_next, c_next, h_next): # a. Solve the keeper problem shape = (2, np.size(par.grid_m) ) # Row for each state of housing - move to model.py file # Intialize v_keep = np.zeros(shape) + np.nan c_keep = np.zeros(shape) + np.nan h_keep = np.zeros(shape) + np.nan # Loop over housing states for n in range(2): # Loop over asset grid for m_i, m in enumerate(par.grid_m): # Use euler equation v_keep[n, m_i] = -res.fun c_keep[n, m_i] = res.x h_keep[n, m_i] = n # b. Solve the adjuster problem # Intialize v_adj = np.zeros(shape) + np.nan c_adj = np.zeros(shape) + np.nan h_adj = np.zeros(shape) + np.nan # Loop over housing state for n in range(2): # Housing choice is reverse of state n if adjusting h = 1 - n # Loop over asset grid for m_i, m in enumerate(par.grid_m): # If adjustment is not possible if n == 0 and m < par.ph: v_adj[n, m_i] = -np.inf c_adj[n, m_i] = 0 h_adj[n, m_i] = np.nan else: # Assets available after adjusting x = m - par.ph * (h - n) # Value of choice v_adj[n, m_i] = tools.interp_linear_1d_scalar( par.grid_m, v_keep[h, :], x) c_adj[n, m_i] = tools.interp_linear_1d_scalar( par.grid_m, c_keep[h, :], x) h_adj[n, m_i] = h # c. Combine solutions # Loop over asset grid again for n in range(2): for m_i, m in enumerate(par.grid_m): # If keeping is optimal if v_keep[n, m_i] > v_adj[n, m_i]: sol.v[n, m_i] = v_keep[n, m_i] sol.c[n, m_i] = c_keep[n, m_i] sol.h[n, m_i] = n # If ajusting is optimal else: sol.v[n, m_i] = v_adj[n, m_i] sol.c[n, m_i] = c_adj[n, m_i] sol.h[n, m_i] = 1 - n return sol