def compute_q(sol, par, t, t_plus): """ compute the post-decision function q """ # unpack (helps numba optimize) q = sol.q # loop over delta for idelta in prange(par.Ndelta): # clean-up q[t, idelta, :] = 0 # temp m_plus = np.empty(par.Na) c_plus = np.empty(par.Na) # loop over shock for ishock in range(par.Nshocks): # i. shocks psi = par.psi[ishock] psi_w = par.psi_w[ishock] xi = par.xi[ishock] xi_w = par.xi_w[ishock] weight = psi_w * xi_w # ii. next-period extra income component delta_plus = par.grid_delta[idelta] / (psi * par.G) if t == 0: delta_plus *= par.zeta # ii. next-period cash-on-hand for ia in range(par.Na): m_plus[ia] = par.R * par.grid_a[ia] / (psi * par.G) + xi # iii. next-period consumption if par.Ndelta > 1: prep = linear_interp.interp_2d_prep(par.grid_delta, delta_plus, par.Na) else: prep = linear_interp.interp_1d_prep(par.Na) if par.Ndelta > 1: linear_interp.interp_2d_only_last_vec_mon( prep, par.grid_delta, par.grid_m, sol.c[t_plus], delta_plus, m_plus, c_plus) else: linear_interp.interp_1d_vec_mon(prep, par.grid_m, sol.c[t_plus, idelta], m_plus, c_plus) # iv. accumulate all for ia in range(par.Na): q[t, idelta, ia] += weight * par.R * par.beta * utility.marg_func( par.G * psi * c_plus[ia], par)
def compute_wq(t, sol, par, compute_w=False, compute_q=False): """ compute the post-decision functions w and/or q """ # this is a variant of Algorithm 5 in Druedahl (2019): A Guide to Solve Non-Convex Consumption-Saving Problems # unpack (helps numba optimize) w = sol.w q = sol.q # loop over outermost post-decision state for ip in prange(par.Np): # in parallel # a. permanent income p = par.grid_p[ip] # b. allocate containers and initialize at zero m_plus = np.empty(par.Na) if compute_w: w[ip, :] = 0 v_plus = np.empty(par.Na) if compute_q: q[ip, :] = 0 c_plus = np.empty(par.Na) # c. loop over shocks and then end-of-period assets for ishock in range(par.Nshocks): # i. shocks psi = par.psi[ishock] psi_w = par.psi_w[ishock] xi = par.xi[ishock] xi_w = par.xi_w[ishock] # ii. next-period income p_plus = p * psi y_plus = p_plus * xi # iii. prepare interpolation in p direction prep = linear_interp.interp_2d_prep(par.grid_p, p_plus, par.Na) # iv. weight weight = psi_w * xi_w # v. next-period cash-on-hand and interpolate for ia in range(par.Na): m_plus[ia] = par.R * par.grid_a[ia] + y_plus # v_plus if compute_w: linear_interp.interp_2d_only_last_vec_mon( prep, par.grid_p, par.grid_m, sol.v[t + 1], p_plus, m_plus, v_plus) # c_plus if compute_q: linear_interp.interp_2d_only_last_vec_mon( prep, par.grid_p, par.grid_m, sol.c[t + 1], p_plus, m_plus, c_plus) # vi. accumulate all if compute_w: for ia in range(par.Na): w[ip, ia] += weight * par.beta * v_plus[ia] if compute_q: for ia in range(par.Na): q[ip, ia] += weight * par.R * par.beta * utility.marg_func( c_plus[ia], par)
def compute(t, sol, par, G2EGM=True): # unpack w = sol.w[t] wa = sol.wa[t] if G2EGM: wb = sol.wb[t] # loop over outermost post-decision state for i_b in range(par.Nb_pd): # a. initialize w[i_b, :] = 0 wa[i_b, :] = 0 if G2EGM: wb[i_b, :] = 0 # b. working memoery inv_v_plus = np.zeros(par.Na_pd) inv_vm_plus = np.zeros(par.Na_pd) if G2EGM: inv_vn_plus = np.zeros(par.Na_pd) inv_v_ret_plus = np.zeros(par.Na_pd) inv_vm_ret_plus = np.zeros(par.Na_pd) if G2EGM: inv_vn_ret_plus = np.zeros(par.Na_pd) # c. loop over shocks for i_eta in range(par.Neta): # i. next period states m_plus = par.Ra * par.grid_a_pd + par.eta[i_eta] n_plus = par.Rb * par.grid_b_pd[i_b] m_plus_ret = m_plus + n_plus # ii. prepare interpolation in p direction prep = linear_interp.interp_2d_prep(par.grid_n, n_plus, par.Na_pd) prep_ret = linear_interp.interp_1d_prep(par.Na_pd) # iii. interpolations # work linear_interp.interp_2d_only_last_vec_mon(prep, par.grid_n, par.grid_m, sol.inv_v[t + 1], n_plus, m_plus, inv_v_plus) linear_interp.interp_2d_only_last_vec_mon_rep( prep, par.grid_n, par.grid_m, sol.inv_vm[t + 1], n_plus, m_plus, inv_vm_plus) if G2EGM: linear_interp.interp_2d_only_last_vec_mon_rep( prep, par.grid_n, par.grid_m, sol.inv_vn[t + 1], n_plus, m_plus, inv_vn_plus) # retire linear_interp.interp_1d_vec_mon(prep_ret, sol.m_ret[t + 1], sol.inv_v_ret[t + 1], m_plus_ret, inv_v_ret_plus) linear_interp.interp_1d_vec_mon_rep(prep_ret, sol.m_ret[t + 1], sol.inv_vm_ret[t + 1], m_plus_ret, inv_vm_ret_plus) if G2EGM: linear_interp.interp_1d_vec_mon_rep(prep_ret, sol.m_ret[t + 1], sol.inv_vn_ret[t + 1], m_plus_ret, inv_vn_ret_plus) # iv. accumulate for i_a in range(par.Na_pd): if inv_v_ret_plus[i_a] > inv_v_plus[i_a]: w_now = -1.0 / inv_v_ret_plus[i_a] wa_now = 1.0 / inv_vm_ret_plus[i_a] if G2EGM: wb_now = 1.0 / inv_vn_ret_plus[i_a] else: w_now = -1.0 / inv_v_plus[i_a] wa_now = 1.0 / inv_vm_plus[i_a] if G2EGM: wb_now = 1.0 / inv_vn_plus[i_a] w[i_b, i_a] += par.w_eta[i_eta] * par.beta * w_now wa[i_b, i_a] += par.w_eta[i_eta] * par.Ra * par.beta * wa_now if G2EGM: wb[i_b, i_a] += par.w_eta[i_eta] * par.Rb * par.beta * wb_now
def compute_wq(t,sol,par,compute_q=False): """ compute the post-decision functions w and/or q """ # unpack inv_w = sol.inv_w[t] q = sol.q[t] # loop over outermost post-decision state for i_p in prange(par.Np): # allocate temporary containers m_plus = np.zeros(par.Na) # container, same lenght as grid_a x_plus = np.zeros(par.Na) w = np.zeros(par.Na) inv_v_keep_plus = np.zeros(par.Na) inv_marg_u_keep_plus = np.zeros(par.Na) inv_v_adj_plus = np.zeros(par.Na) inv_marg_u_adj_plus = np.zeros(par.Na) # loop over other outer post-decision states for i_n in range(par.Nn): # a. permanent income and durable stock p = par.grid_p[i_p] n = par.grid_n[i_n] # b. initialize at zero for i_a in range(par.Na): w[i_a] = 0.0 q[i_p,i_n,i_a] = 0.0 # c. loop over shocks and then end-of-period assets for ishock in range(par.Nshocks): # i. shocks psi_plus = par.psi[ishock] psi_plus_w = par.psi_w[ishock] xi_plus = par.xi[ishock] xi_plus_w = par.xi_w[ishock] # ii. next-period income and durables p_plus = trans.p_plus_func(p,psi_plus,par) n_plus = trans.n_plus_func(n,par) # iii. prepare interpolators prep_keep = linear_interp.interp_3d_prep(par.grid_p,par.grid_n,p_plus,n_plus,par.Na) prep_adj = linear_interp.interp_2d_prep(par.grid_p,p_plus,par.Na) # iv. weight weight = psi_plus_w*xi_plus_w # v. next-period cash-on-hand and total resources for i_a in range(par.Na): m_plus[i_a] = trans.m_plus_func(par.grid_a[i_a],p_plus,xi_plus,par) x_plus[i_a] = trans.x_plus_func(m_plus[i_a],n_plus,par) # vi. interpolate linear_interp.interp_3d_only_last_vec_mon(prep_keep,par.grid_p,par.grid_n,par.grid_m,sol.inv_v_keep[t+1],p_plus,n_plus,m_plus,inv_v_keep_plus) linear_interp.interp_2d_only_last_vec_mon(prep_adj,par.grid_p,par.grid_x,sol.inv_v_adj[t+1],p_plus,x_plus,inv_v_adj_plus) if compute_q: linear_interp.interp_3d_only_last_vec_mon_rep(prep_keep,par.grid_p,par.grid_n,par.grid_m,sol.inv_marg_u_keep[t+1],p_plus,n_plus,m_plus,inv_marg_u_keep_plus) linear_interp.interp_2d_only_last_vec_mon_rep(prep_adj,par.grid_p,par.grid_x,sol.inv_marg_u_adj[t+1],p_plus,x_plus,inv_marg_u_adj_plus) # vii. max and accumulate if compute_q: for i_a in range(par.Na): keep = inv_v_keep_plus[i_a] > inv_v_adj_plus[i_a] if keep: v_plus = -1/inv_v_keep_plus[i_a] marg_u_plus = 1/inv_marg_u_keep_plus[i_a] else: v_plus = -1/inv_v_adj_plus[i_a] marg_u_plus = 1/inv_marg_u_adj_plus[i_a] w[i_a] += weight*par.beta*v_plus q[i_p,i_n,i_a] += weight*par.beta*par.R*marg_u_plus else: for i_a in range(par.Na): w[i_a] += weight*par.beta*(-1.0/np.fmax(inv_v_keep_plus[i_a],inv_v_adj_plus[i_a])) # d. transform post decision value function for i_a in range(par.Na): inv_w[i_p,i_n,i_a] = -1/w[i_a]
def compute_w(beta,R,delta,inv_v_keep_p,inv_v_adj_p,Np,Nn,Nm,Nb,grid_p,grid_n,grid_m,grid_x,grid_b,p_trans,n_max,tau,sigma_eps,P_d_p):# P_d_p """ compute the post-decision function w """ # unpack post_shape = (Np,Nn,Nb) inv_w = np.nan*np.zeros(post_shape) # loop over outermost post-decision state for i_p in prange(Np): # allocate temporary containers m_plus = np.zeros(Nb) # container, same lenght as grid_b x_plus = np.zeros(Nb) w = np.zeros(Nb) inv_v_keep_plus = np.zeros(Nb) inv_v_adj_plus = np.zeros(Nb) # loop over other outer post-decision states for i_n in range(Nn): # a. permanent income and durable stock n = grid_n[i_n] # b. initialize at zero for i_b in range(Nb): w[i_b] = 0.0 # c. loop over shocks and then end-of-period assets for ishock in range(Np): # i. shocks weight = p_trans[i_p,ishock] # ii. next-period income and durables n_plus = (1-delta)*n n_plus = np.fmin(n_plus,n_max) # upper bound p_plus = grid_p[ishock] # iii. prepare interpolators prep_keep = linear_interp.interp_3d_prep(grid_p,grid_n,p_plus,n_plus,Nb) prep_adj = linear_interp.interp_2d_prep(grid_p,p_plus,Nb) # v. next-period cash-on-hand and total resources for i_b in range(Nb): m_plus[i_b] = R*grid_b[i_b]+ p_plus - grid_b[0] # borrowing allowed x_plus[i_b] = m_plus[i_b] + P_d_p*(1-tau)*n_plus # *pris # vi. interpolate linear_interp.interp_3d_only_last_vec_mon(prep_keep,grid_p,grid_n,grid_m,inv_v_keep_p,p_plus,n_plus,m_plus,inv_v_keep_plus) # t+1 v linear_interp.interp_2d_only_last_vec_mon(prep_adj,grid_p,grid_x,inv_v_adj_p,p_plus,x_plus,inv_v_adj_plus) # t+1 v # vii. max and accumulate for i_b in range(Nb): if inv_v_keep_plus[i_b] == 0: v_keep =-9999.0 else: v_keep=-1.0/inv_v_keep_plus[i_b] if inv_v_adj_plus[i_b] == 0: v_adj=-9999.0 else: v_adj=-1.0/inv_v_adj_plus[i_b] v_max = np.fmax(v_keep,v_adj) val = v_max + sigma_eps*np.log(np.exp((v_keep-v_max)/sigma_eps) + np.exp((v_adj-v_max)/sigma_eps)) w[i_b] += weight*beta*(val) # d. transform post decision value function for i_b in range(Nb): inv_w[i_p,i_n,i_b] = -1/w[i_b] return inv_w