def lifecycle(sim, sol, par): """ simulate full life-cycle """ # unpack p = sim.p n = sim.n m = sim.m x = sim.x c = sim.c d = sim.d a = sim.a discrete = sim.discrete for t in range(par.simT): for i in prange(par.simN): # a. beginning of period states if t == 0: p[t, i] = trans.p_plus_func(sim.p0[i], sim.psi[t, i], par) n[t, i] = trans.n_plus_func(sim.d0[i], par) m[t, i] = trans.m_plus_func(sim.a0[i], p[t, i], sim.xi[t, i], par) else: p[t, i] = trans.p_plus_func(p[t - 1, i], sim.psi[t, i], par) n[t, i] = trans.n_plus_func(d[t - 1, i], par) m[t, i] = trans.m_plus_func(a[t - 1, i], p[t, i], sim.xi[t, i], par) x[t, i] = trans.x_plus_func(m[t, i], n[t, i], par) optimal_choice(t, p[t, i], n[t, i], m[t, i], x[t, i], discrete[t, i:], d[t, i:], c[t, i:], a[t, i:], sol, par)
def lifecycle(sim, sol, par): """ simulate full life-cycle """ # unpack p = sim.p n = sim.n n1 = sim.n1 n2 = sim.n2 m = sim.m c = sim.c d = sim.d d1 = sim.d1 d2 = sim.d2 a = sim.a discrete = sim.discrete for t in range(par.T): for i in prange(par.simN): # a. beginning of period states if t == 0: p[t, i] = trans.p_plus_func(sim.p0[i], sim.psi[t, i], par) if par.do_2d: n1[t, i] = trans.n1_plus_func(sim.d10[i], par) n2[t, i] = trans.n2_plus_func(sim.d20[i], par) else: n[t, i] = trans.n_plus_func(sim.d0[i], par) m[t, i] = trans.m_plus_func(sim.a0[i], p[t, i], sim.xi[t, i], par) else: p[t, i] = trans.p_plus_func(p[t - 1, i], sim.psi[t, i], par) if par.do_2d: n1[t, i] = trans.n1_plus_func(d1[t - 1, i], par) n2[t, i] = trans.n2_plus_func(d2[t - 1, i], par) else: n[t, i] = trans.n_plus_func(d[t - 1, i], par) m[t, i] = trans.m_plus_func(a[t - 1, i], p[t, i], sim.xi[t, i], par) # b. optimal choices and post decision states if par.do_2d: optimal_choice_2d(t, p[t, i], n1[t, i], n2[t, i], m[t, i], discrete[t, i:], d1[t, i:], d2[t, i:], c[t, i:], a[t, i:], sol, par) else: optimal_choice(t, p[t, i], n[t, i], m[t, i], discrete[t, i:], d[t, i:], c[t, i:], a[t, i:], sol, par)
def euler_errors(sim, sol, par): # unpack euler_error = sim.euler_error euler_error_c = sim.euler_error_c for i in prange(par.simN): discrete_plus = np.zeros(1) d_plus = np.zeros(1) c_plus = np.zeros(1) a_plus = np.zeros(1) for t in range(par.simT - 1): constrained = sim.a[t, i] < par.euler_cutoff if constrained: euler_error[t, i] = np.nan euler_error_c[t, i] = np.nan continue else: LHS = utility.marg_func(sim.c[t, i], sim.d[t, i], par) RHS = 0.0 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 states p_plus = trans.p_plus_func(sim.p[t, i], psi, par) n_plus = trans.n_plus_func(sim.d[t, i], par) m_plus = trans.m_plus_func(sim.a[t, i], p_plus, xi, par) x_plus = trans.x_plus_func(m_plus, n_plus, par) # iii. weight weight = psi_w * xi_w # iv. next-period choices optimal_choice(t + 1, p_plus, n_plus, m_plus, x_plus, discrete_plus, d_plus, c_plus, a_plus, sol, par) # v. next-period marginal utility RHS += weight * par.beta * par.R * utility.marg_func( c_plus[0], d_plus[0], par) euler_error[t, i] = LHS - RHS euler_error_c[t, i] = sim.c[t, i]
def value_of_choice(t, c, d, p, x, inv_v_keep, inv_v_adj, par): # a. end-of-period-assets a = x - c - d # b. continuation value w = 0 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 states p_plus = trans.p_plus_func(p, psi, par) n_plus = trans.n_plus_func(d, par) m_plus = trans.m_plus_func(a, p_plus, xi, par) x_plus = trans.x_plus_func(m_plus, n_plus, par) # iii. weight weight = psi_w * xi_w # iv. update inv_v_plus_keep_now = linear_interp.interp_3d(par.grid_p, par.grid_n, par.grid_m, inv_v_keep[t + 1], p_plus, n_plus, m_plus) inv_v_plus_adj_now = linear_interp.interp_2d(par.grid_p, par.grid_x, inv_v_adj[t + 1], p_plus, x_plus) v_plus_now = -np.inf # huge negative value if inv_v_plus_keep_now > inv_v_plus_adj_now and inv_v_plus_keep_now > 0: v_plus_now = -1 / inv_v_plus_keep_now elif inv_v_plus_adj_now > 0: v_plus_now = -1 / inv_v_plus_adj_now w += weight * par.beta * v_plus_now # v. total value v = utility.func(c, d, par) + w return v # we are minimizing
def euler_errors(sim, sol, par): # unpack euler_error = sim.euler_error euler_error_c = sim.euler_error_c for i in prange(par.simN): discrete_plus = np.zeros(1) d_plus = np.zeros(1) d1_plus = np.zeros(1) d2_plus = np.zeros(1) c_plus = np.zeros(1) a_plus = np.zeros(1) for t in range(par.T - 1): constrained = sim.a[t, i] < par.euler_cutoff if constrained: euler_error[t, i] = np.nan euler_error_c[t, i] = np.nan continue else: RHS = 0.0 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 states p_plus = trans.p_plus_func(sim.p[t, i], psi, par) if par.do_2d: n1_plus = trans.n1_plus_func(sim.d1[t, i], par) n2_plus = trans.n2_plus_func(sim.d2[t, i], par) else: n_plus = trans.n_plus_func(sim.d[t, i], par) m_plus = trans.m_plus_func(sim.a[t, i], p_plus, xi, par) # iii. weight weight = psi_w * xi_w # iv. next-period choices if par.do_2d: optimal_choice_2d(t + 1, p_plus, n1_plus, n2_plus, m_plus, discrete_plus, d1_plus, d2_plus, c_plus, a_plus, sol, par) else: optimal_choice(t + 1, p_plus, n_plus, m_plus, discrete_plus, d_plus, c_plus, a_plus, sol, par) # v. next-period marginal utility if par.do_2d: RHS += weight * par.beta * par.R * utility.marg_func_2d( c_plus[0], d1_plus[0], d2_plus[0], par) else: RHS += weight * par.beta * par.R * utility.marg_func( c_plus[0], d_plus[0], par) if par.do_2d: euler_error[t, i] = sim.c[t, i] - utility.inv_marg_func_2d( RHS, sim.d1[t, i], sim.d2[t, i], par) else: euler_error[t, i] = sim.c[t, i] - utility.inv_marg_func( RHS, sim.d[t, i], par) euler_error_c[t, i] = sim.c[t, i]
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]