def deviate_d_con(valt, n, c, a, w, par): for i_b in range(par.Nb_pd): for i_a in range(par.Na_pd): # a. choices d_x = par.delta_con * c[i_b, i_a] c_x = (1.0 - par.delta_con) * c[i_b, i_a] # b. post-decision states b_x = n[i_b, i_a] + d_x + pens.func(d_x, par) if not np.imag(b_x) == 0: valt[i_b, i_a] = np.nan continue # c. value w_x = linear_interp.interp_2d(par.grid_b_pd, par.grid_a_pd, w, b_x, a[i_b, i_a]) v_x = utility.func(c_x, par) + w_x if not np.imag(v_x) == 0: valt[i_b, i_a] = np.nan else: valt[i_b, i_a] = v_x
def inv_mn_and_v(c, d, a, b, w, par): v = utility.func(c, par) + w m = a + c + d n = b - d - pens.func(d, par) return m, n, v
def solve(t,sol,par): w = sol.w[t] wa = sol.wa[t] wb = sol.wb[t] # a. solve each segment solve_ucon(sol.ucon_c[t,:,:],sol.ucon_d[t,:,:],sol.ucon_v[t,:,:],w,wa,wb,par) solve_dcon(sol.dcon_c[t,:,:],sol.dcon_d[t,:,:],sol.dcon_v[t,:,:],w,wa,par) solve_acon(sol.acon_c[t,:,:],sol.acon_d[t,:,:],sol.acon_v[t,:,:],w,wb,par) solve_con(sol.con_c[t,:,:],sol.con_d[t,:,:],sol.con_v[t,:,:],w,par) # b. upper envelope seg_max = np.zeros(4) for i_n in range(par.Nn): for i_m in range(par.Nm): # i. find max seg_max[0] = sol.ucon_v[t,i_n,i_m] seg_max[1] = sol.dcon_v[t,i_n,i_m] seg_max[2] = sol.acon_v[t,i_n,i_m] seg_max[3] = sol.con_v[t,i_n,i_m] i = np.argmax(seg_max) # ii. over-arching optimal choices sol.inv_v[t,i_n,i_m] = -1.0/seg_max[i] if i == 0: sol.c[t,i_n,i_m] = sol.ucon_c[t,i_n,i_m] sol.d[t,i_n,i_m] = sol.ucon_d[t,i_n,i_m] elif i == 1: sol.c[t,i_n,i_m] = sol.dcon_c[t,i_n,i_m] sol.d[t,i_n,i_m] = sol.dcon_d[t,i_n,i_m] elif i == 2: sol.c[t,i_n,i_m] = sol.acon_c[t,i_n,i_m] sol.d[t,i_n,i_m] = sol.acon_d[t,i_n,i_m] elif i == 3: sol.c[t,i_n,i_m] = sol.con_c[t,i_n,i_m] sol.d[t,i_n,i_m] = sol.con_d[t,i_n,i_m] # c. derivatives # i. m vm = utility.marg_func(sol.c[t],par) sol.inv_vm[t,:,:] = 1.0/vm # ii. n a = par.grid_m_nd - sol.c[t] - sol.d[t] b = par.grid_n_nd + sol.d[t] + pens.func(sol.d[t],par) wb_now = np.zeros(a.shape) linear_interp.interp_2d_vec(par.grid_b_pd,par.grid_a_pd,wb,b.ravel(),a.ravel(),wb_now.ravel()) vn = wb_now sol.inv_vn[t,:,:] = 1.0/vn
def obj_outer(d,n,m,t,sol,par): """ evaluate bellman equation """ # a. cash-on-hand m_pure_c = m-d # b. durables n_pure_c = n + d + pens.func(d,par) # c. value-of-choice return -linear_interp.interp_2d(par.grid_b_pd,par.grid_l,sol.inv_v_pure_c[t],n_pure_c,m_pure_c) # we are minimizing
def solve_outer(t,sol,par): # unpack output inv_v = sol.inv_v[t] inv_vm = sol.inv_vm[t] c = sol.c[t] d = sol.d[t] # loop over outer states for i_n in range(par.Nn): n = par.grid_n[i_n] # loop over m state for i_m in range(par.Nm): m = par.grid_m[i_m] # a. optimal choice d_low = 1e-8 d_high = m-1e-8 d[i_n,i_m] = golden_section_search.optimizer(obj_outer,d_low,d_high,args=(n,m,t,sol,par),tol=1e-8) # b. optimal value n_pure_c = n + d[i_n,i_m] + pens.func(d[i_n,i_m],par) m_pure_c = m - d[i_n,i_m] c[i_n,i_m] = np.fmin(linear_interp.interp_2d(par.grid_b_pd,par.grid_l,sol.c_pure_c[t],n_pure_c,m_pure_c),m_pure_c) inv_v[i_n,i_m] = -obj_outer(d[i_n,i_m],n,m,t,sol,par) # c. dcon obj_dcon = -obj_outer(0,n,m,t,sol,par) if obj_dcon > inv_v[i_n,i_m]: c[i_n,i_m] = linear_interp.interp_2d(par.grid_b_pd,par.grid_l,sol.c_pure_c[t],n,m) d[i_n,i_m] = 0 inv_v[i_n,i_m] = obj_dcon # d. con w = linear_interp.interp_2d(par.grid_b_pd,par.grid_a_pd,sol.w[t],n,0) obj_con = -1.0/(utility.func(m,par) + w) if obj_con > inv_v[i_n,i_m]: c[i_n,i_m] = m d[i_n,i_m] = 0 inv_v[i_n,i_m] = obj_con # e. derivative inv_vm[i_n,i_m] = 1.0/utility.marg_func(c[i_n,i_m],par)
def euler(sim, sol, par): euler = sim.euler # a. grids min_m = 0.50 min_n = 0.01 m_max = 5.00 n_max = 5.00 n_grid = np.linspace(min_n, n_max, par.eulerK) m_grid = np.linspace(min_m, m_max, par.eulerK) # b. loop over time for t in range(par.T - 1): for i_n in range(par.eulerK): for i_m in range(par.eulerK): # i. states n = n_grid[i_n] m = m_grid[i_m] m_retire = m_grid[i_m] + n_grid[i_n] # ii. discrete choice inv_v_retire = linear_interp.interp_1d(sol.m_ret[t], sol.inv_v_ret[t], m_retire) inv_v = linear_interp.interp_2d(par.grid_n, par.grid_m, sol.inv_v[t], n, m) if inv_v_retire > inv_v: continue # iii. continuous choice c = np.fmin( linear_interp.interp_2d(par.grid_n, par.grid_m, sol.c[t], n, m), m) d = np.fmax( linear_interp.interp_2d(par.grid_n, par.grid_m, sol.d[t], n, m), 0) a = m - c - d b = n + d + pens.func(d, par) if a < 0.001: continue # iv. shocks RHS = 0 for i_eta in range(par.Neta): # o. state variables n_plus = par.Rb * b m_plus = par.Ra * a + par.eta[i_eta] m_retire_plus = m_plus + n_plus # oo. discrete choice inv_v_retire = linear_interp.interp_1d( sol.m_ret[t + 1], sol.inv_v_ret[t + 1], m_retire_plus) inv_v = linear_interp.interp_2d(par.grid_n, par.grid_m, sol.inv_v[t + 1], n_plus, m_plus) # ooo. continous choice if inv_v_retire > inv_v: c_plus = np.fmin( linear_interp.interp_1d(sol.m_ret[t + 1], sol.c_ret[t + 1], m_retire_plus), m_retire_plus) else: c_plus = np.fmin( linear_interp.interp_2d(par.grid_n, par.grid_m, sol.c[t + 1], n_plus, m_plus), m_plus) # oooo. accumulate RHS += par.w_eta[ i_eta] * par.beta * par.Ra * utility.marg_func( c_plus, par) # v. euler error euler_raw = c - utility.inv_marg_func(RHS, par) euler[t, i_m, i_n] = np.log10(np.abs(euler_raw / c) + 1e-16)
def upperenvelope(out_c, out_d, out_v, holes, i_a, i_b, tri, m, n, c, d, v, Na, Nb, valid, num, w, par): # a. simplex in (a,b)-space (or similar with constrained choices) i_b_1 = i_b i_a_1 = i_a if i_b == Nb - 1: return i_b_2 = i_b + 1 i_a_2 = i_a i_b_3 = -1 # to be overwritten i_a_3 = -1 # to be overwritten if tri == 0: if i_a == 0 or i_b == Nb - 1: return i_b_3 = i_b + 1 i_a_3 = i_a - 1 else: if i_a == Na - 1: return i_b_3 = i_b i_a_3 = i_a + 1 if ~valid[i_b_1, i_a_1] or ~valid[i_b_2, i_a_2] or ~valid[i_b_3, i_a_3]: return # b. simplex in (m,n)-space m1 = m[i_b_1, i_a_1] m2 = m[i_b_2, i_a_2] m3 = m[i_b_3, i_a_3] n1 = n[i_b_1, i_a_1] n2 = n[i_b_2, i_a_2] n3 = n[i_b_3, i_a_3] # c. boundary box values and indices in common grid m_max = np.fmax(m1, np.fmax(m2, m3)) m_min = np.fmin(m1, np.fmin(m2, m3)) n_max = np.fmax(n1, np.fmax(n2, n3)) n_min = np.fmin(n1, np.fmin(n2, n3)) im_low = 0 if m_min >= 0: im_low = linear_interp.binary_search(0, par.Nm, par.grid_m, m_min) im_high = linear_interp.binary_search(0, par.Nm, par.grid_m, m_max) + 1 in_low = 0 if n_min >= 0: in_low = linear_interp.binary_search(0, par.Nn, par.grid_n, n_min) in_high = linear_interp.binary_search(0, par.Nn, par.grid_n, n_max) + 1 # correction to allow for more extrapolation im_low = np.fmax(im_low - par.egm_extrap_add, 0) im_high = np.fmin(im_high + par.egm_extrap_add, par.Nm) in_low = np.fmax(in_low - par.egm_extrap_add, 0) in_high = np.fmin(in_high + par.egm_extrap_add, par.Nn) # d. prepare barycentric interpolation denom = (n2 - n3) * (m1 - m3) + (m3 - m2) * (n1 - n3) # e. loop through common grid nodes in interior of bounding box for i_n in range(in_low, in_high): for i_m in range(im_low, im_high): # i. common grid values m_now = par.grid_m[i_m] n_now = par.grid_n[i_n] # ii. barycentric coordinates w1 = ((n2 - n3) * (m_now - m3) + (m3 - m2) * (n_now - n3)) / denom w2 = ((n3 - n1) * (m_now - m3) + (m1 - m3) * (n_now - n3)) / denom w3 = 1 - w1 - w2 # iii. exit if too much outside simplex if w1 < par.egm_extrap_w or w2 < par.egm_extrap_w or w3 < par.egm_extrap_w: continue # iv. interpolate choices if num == 1: # ucon, interpolate c and d c_interp = w1 * c[i_b_1, i_a_1] + w2 * c[ i_b_2, i_a_2] + w3 * c[i_b_3, i_a_3] d_interp = w1 * d[i_b_1, i_a_1] + w2 * d[ i_b_2, i_a_2] + w3 * d[i_b_3, i_a_3] a_interp = m_now - c_interp - d_interp b_interp = n_now + d_interp + pens.func(d_interp, par) elif num == 2: # dcon, interpolate c c_interp = w1 * c[i_b_1, i_a_1] + w2 * c[ i_b_2, i_a_2] + w3 * c[i_b_3, i_a_3] d_interp = 0.0 a_interp = m_now - c_interp - d_interp b_interp = n_now # d_interp = 0 elif num == 3: # acon, interpolate d a_interp = 0.0 d_interp = w1 * d[i_b_1, i_a_1] + w2 * d[ i_b_2, i_a_2] + w3 * d[i_b_3, i_a_3] c_interp = m_now - a_interp - d_interp b_interp = n_now + d_interp + pens.func(d_interp, par) if c_interp <= 0.0 or d_interp < 0.0 or a_interp < 0 or b_interp < 0: continue # v. value-of-choice w_interp = linear_interp.interp_2d(par.grid_b_pd, par.grid_a_pd, w, b_interp, a_interp) v_interp = utility.func(c_interp, par) + w_interp # vi. update if max if v_interp > out_v[i_n, i_m]: out_v[i_n, i_m] = v_interp out_c[i_n, i_m] = c_interp out_d[i_n, i_m] = d_interp holes[i_n, i_m] = 0