def euler_sys(guesses, *args): ''' -------------------------------------------------------------------- Specify the system of Euler Equations characterizing the household problem. -------------------------------------------------------------------- INPUTS: guesses = (2S-1,) vector, guess at labor supply and savings decisions args = length 14 tuple, (r, w, beta, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff, S, SS_tol) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: hh.get_n_errors() hh.get_n_errors() hh.get_cons() OBJECTS CREATED WITHIN FUNCTION: r = scalar > 0, guess at steady-state interest rate w = scalar > 0, guess at steady-state wage beta = scalar in (0,1), discount factor sigma = scalar >= 1, coefficient of relative risk aversion l_tilde = scalar > 0, per-period time endowment for every agent chi_n_vec = (S,) vector, values for chi^n_s b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor A = scalar > 0, total factor productivity parameter in firms' production function diff = boolean, =True if simple difference Euler errors, otherwise percent deviation Euler errors S = integer >= 3, number of periods in individual lifetime SS_tol = scalar > 0, tolerance level for steady-state fsolve nvec = (S,) vector, lifetime labor supply (n1, n2, ...nS) bvec = (S,) vector, lifetime savings (b1, b2, ...bS) with b1=0 cvec = (S,) vector, lifetime consumption (c1, c2, ...cS) b_sp1 = = (S,) vector, lifetime savings (b1, b2, ...bS) with bS=0 n_errors = (S,) vector, labor supply Euler errors b_errors = (S-1,) vector, savings Euler errors FILES CREATED BY THIS FUNCTION: None RETURNS: array of n_errors and b_errors -------------------------------------------------------------------- ''' (r, w, beta, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff, S) = args nvec = guesses[:S] bvec1 = guesses[S:] b_s = np.append(0.0, bvec1) b_sp1 = np.append(bvec1, 0.0) cvec = hh.get_cons(r, w, b_s, b_sp1, nvec) n_args = (w, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff, cvec) n_errors = hh.get_n_errors(nvec, *n_args) b_args = (r, beta, sigma, diff) b_errors = hh.get_b_errors(cvec, *b_args) errors = np.append(n_errors, b_errors) return errors
def SS_EulErrs(bvec, *args): ''' -------------------------------------------------------------------- -------------------------------------------------------------------- INPUTS: bvec = (S-1,) vector, lifetime savings args = length 7 tuple, (nvec, beta, sigma, A, alpha, delta, EulDiff) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: aggr.get_K() aggr.get_L() firms.get_r() firms.get_w() hh.get_cons() hh.get_b_errors() OBJECTS CREATED WITHIN FUNCTION: nvec = (S,) vector, exogenous lifetime labor supply n_s beta = scalar in (0,1), discount factor for each model per sigma = scalar > 0, coefficient of relative risk aversion A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], model-period depreciation rate of capital EulDiff = boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 K = scalar > 0, aggregate capital stock K_cstr = boolean, =True if K < epsilon L = scalar > 0, exogenous aggregate labor r_params = length 3 tuple, (A, alpha, delta) r = scalar > 0, interest rate w_params = length 2 tuple, (A, alpha) w = scalar > 0, wage c_args = length 3 tuple, (nvec, r, w) cvec = (S,) vector, household consumption c_s b_args = length 4 tuple, (beta, sigma, r, EulDiff) errors = (S-1,) vector, savings Euler errors given bvec FILES CREATED BY THIS FUNCTION: None RETURNS: errors -------------------------------------------------------------------- ''' nvec, beta, sigma, A, alpha, delta, EulDiff = args K, K_cstr = aggr.get_K(bvec) L = aggr.get_L(nvec) r_params = (A, alpha, delta) r = firms.get_r(K, L, r_params) w_params = (A, alpha) w = firms.get_w(K, L, w_params) c_args = (nvec, r, w) cvec = hh.get_cons(bvec, 0.0, c_args) b_args = (beta, sigma, r, EulDiff) errors = hh.get_b_errors(cvec, b_args) return errors
def LfEulerSys(bvec, *args): ''' -------------------------------------------------------------------- Generates vector of all Euler errors for a given bvec, which errors characterize all optimal lifetime decisions, where p is an integer in [2, S] representing the remaining periods of life -------------------------------------------------------------------- INPUTS: bvec = (p-1,) vector, remaining lifetime savings decisions where p is the number of remaining periods args = length 7 tuple, (beta, sigma, beg_wealth, nvec, rpath, wpath, EulDiff) beta = scalar in [0,1), discount factor sigma = scalar > 0, coefficient of relative risk aversion beg_wealth = scalar, wealth at the beginning of first age nvec = (p,) vector, remaining exogenous labor supply rpath = (p,) vector, interest rates over remaining life wpath = (p,) vector, wages rates over remaining life EulDiff = Boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: c6ssf.get_cvec() c6ssf.get_b_errors() OBJECTS CREATED WITHIN FUNCTION: bvec2 = (p,) vector, remaining savings including initial savings cvec = (p,) vector, remaining lifetime consumption levels implied by bvec2 c_cnstr = (p,) Boolean vector, =True if c_{s,t}<=0 b_err_params = length 2 tuple, (beta, sigma) b_err_vec = (p-1,) vector, Euler errors from lifetime consumption vector FILES CREATED BY THIS FUNCTION: None RETURNS: b_err_vec -------------------------------------------------------------------- ''' beta, sigma, beg_wealth, nvec, rpath, wpath, EulDiff = args c_args = (nvec, rpath, wpath) cvec = hh.get_cons(bvec, beg_wealth, c_args) b_args = (beta, sigma, rpath[1:], EulDiff) b_err_vec = hh.get_b_errors(cvec, b_args) return b_err_vec
def firstdoughnutring(guesses, args): ''' Solves the first entries of the upper triangle of the twist doughnut. This is separate from the main TPI function because the the values of b and n are scalars, so it is easier to just have a separate function for these cases. Inputs: guesses = guess for b and n (2x1 list) winit = initial wage rate (scalar) rinit = initial rental rate (scalar) BQinit = initial aggregate bequest (scalar) T_H_init = initial lump sum tax (scalar) initial_b = initial distribution of capital (SxJ array) factor = steady state scaling factor (scalar) j = which ability type is being solved for (scalar) parameters = list of parameters (list) theta = replacement rates (Jx1 array) tau_bq = bequest tax rates (Jx1 array) Output: euler errors (2x1 list) ''' # unpack tuples of parameters r, w, S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec,\ initial_b, diff = args b_splus1 = 0.0 # leave zero savings in last period of life n = float(guesses[1]) b_s = float(initial_b[-1]) # Euler equations cons = hh.get_cons(r, w, b_s, b_splus1, n) b_params = (beta, sigma) error1 = 0.0 #hh.get_b_errors(b_params, r, cons, diff) n_args = (w, cons, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff) error2 = hh.get_n_errors(n, n_args) if n <= 0 or n >= 1: error2 += 1e14 if cons <= 0: error1 += 1e14 return [error1] + [error2]
def get_TPI(params, bvec1, graphs): ''' -------------------------------------------------------------------- Solves for transition path equilibrium using time path iteration (TPI) -------------------------------------------------------------------- INPUTS: params = length 21 tuple, (S, T1, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, K_ss, L_ss, C_ss, maxiter, mindist, TPI_tol, xi, diff, hh_fsolve) bvec1 = (S,) vector, initial period savings distribution graphs = Boolean, =True if want graphs of TPI objects OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: aggr.get_K() get_path() firms.get_r() firms.get_w() get_cnbpath() solve_bn_path() aggr.get_L() aggr.get_Y() aggr.get_C() utils.print_time() OBJECTS CREATED WITHIN FUNCTION: start_time = scalar, current processor time in seconds (float) S = integer in [3,80], number of periods an individual lives T1 = integer > S, number of time periods until steady state is assumed to be reached T2 = integer > T1, number of time periods after which steady-state is forced in TPI beta = scalar in (0,1), discount factor for model period sigma = scalar > 0, coefficient of relative risk aversion l_tilde = scalar > 0, time endowment for each agent each period b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], per-period capital depreciation rt K_ss = scalar > 0, steady-state aggregate capital stock L_ss = scalar > 0, steady-state aggregate labor supply C_ss = scalar > 0, steady-state aggregate consumption maxiter = integer >= 1, Maximum number of iterations for TPI mindist = scalar > 0, convergence criterion for TPI TPI_tol = scalar > 0, tolerance level for TPI root finders xi = scalar in (0,1], TPI path updating parameter diff = Boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 hh_fsolve = boolean, =True if solve inner-loop household problem by choosing c_1 to set final period savings b_{S+1}=0. Otherwise, solve the household problem as multivariate root finder with 2S-1 unknowns and equations K1 = scalar > 0, initial aggregate capital stock K1_cnstr = Boolean, =True if K1 <= 0 Kpath_init = (T2+S-1,) vector, initial guess for the time path of the aggregate capital stock Lpath_init = (T2+S-1,) vector, initial guess for the time path of aggregate labor domain = (T2,) vector, integers from 0 to T2-1 domain2 = (T2,S) array, integers from 0 to T2-1 repeated S times ending_b = (S,) vector, distribution of savings at end of time path initial_b = (S,) vector, distribution of savings in initial period guesses_b = (T2,S) array, initial guess at distribution of savings over the time path ending_b_tail = (S,S) array, distribution of savings for S periods after end of time path guesses_b = (T2+S,S) array, guess at distribution of savings for T2+S periods domain3 = (T2,S) array, integers from 0 to T2-1 repeated S times initial_n = (S,) vector, distribution of labor supply in initial period guesses_n = (T2,S) array, initial guess at distribution of labor supply over the time path ending_n_tail = (S,S) array, distribution of labor supply for S periods after end of time path guesses_n = (T2+S,S) array, guess at distribution of labor supply for T2+S periods guesses = length 2 tuple, initial guesses at distributions of savings and labor supply over the time path iter_TPI = integer >= 0, current iteration of TPI dist = scalar >= 0, distance measure between initial and new paths r_params = length 3 tuple, (A, alpha, delta) w_params = length 2 tuple, (A, alpha) Y_params = length 2 tuple, (A, alpha) cnb_params = length 11 tuple, args to pass into get_cnbpath() rpath = (T2+S-1,) vector, time path of the interest rates wpath = (T2+S-1,) vector, time path of the wages ind = (S,) vector, integers from 0 to S bn_args = length 14 tuple, arguments to be passed to solve_bn_path() cpath = (S, T2+S-1) matrix, time path of distribution of individual consumption c_{s,t} npath = (S, T2+S-1) matrix, time path of distribution of individual labor supply n_{s,t} bpath = (S, T2+S-1) matrix, time path of distribution of individual savings b_{s,t} n_err_path = (S, T2+S-1) matrix, time path of distribution of individual labor supply Euler errors b_err_path = (S, T2+S-1) matrix, time path of distribution of individual savings Euler errors. First column and first row are identically zero bSp1_err_path = (S, T2) matrix, residual last period savings, which should be close to zero in equilibrium. Nonzero elements of matrix should only be in first column and first row Kpath_new = (T2+S-1,) vector, new path of the aggregate capital stock implied by household and firm optimization Kpath_cnstr = (T2+S-1,) Boolean vector, =True if K_t<=0 Lpath_new = (T2+S-1,) vector, new path of the aggregate labor rpath_new = (T2+S-1,) vector, updated time path of interest rate wpath_new = (T2+S-1,) vector, updated time path of the wages Ypath = (T2+S-1,) vector, equilibrium time path of aggregate output (GDP) Y_t Cpath = (T2+S-1,) vector, equilibrium time path of aggregate consumption C_t RCerrPath = (T2+S-2,) vector, equilibrium time path of the resource constraint error: Y_t - C_t - K_{t+1} + (1-delta)*K_t KL_path_new = (2*T2,) vector, appended K_path_new and L_path_new from observation 1 to T2 KL_path_init = (2*T2,) vector, appended K_path_init and L_path_init from observation 1 to T2 Kpath = (T2+S-1,) vector, equilibrium time path of aggregate capital stock K_t Lpath = (T2+S-1,) vector, equilibrium time path of aggregate labor L_t tpi_time = scalar, time to compute TPI solution (seconds) tpi_output = length 14 dictionary, {cpath, npath, bpath, wpath, rpath, Kpath, Lpath, Ypath, Cpath, bSp1_err_path, n_err_path, b_err_path, RCerrPath, tpi_time} FILES CREATED BY THIS FUNCTION: Kpath.png Lpath.png Ypath.png C_aggr_path.png wpath.png rpath.png cpath.png npath.png bpath.png RETURNS: tpi_output -------------------------------------------------------------------- ''' start_time = time.clock() (S, T1, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, K_ss, L_ss, C_ss, b_splus1_ss, n_ss, maxiter, mindist, TPI_tol, xi, diff, hh_fsolve) = params K1, K1_cnstr = aggr.get_K(bvec1) # Create time paths for K and L Kpath_init = np.zeros(T2 + S - 1) Kpath_init[:T1] = get_path(K1, K_ss, T1, 'quadratic') Kpath_init[T1:] = K_ss Lpath_init = L_ss * np.ones(T2 + S - 1) # Make arrays of initial guesses for labor supply and savings domain = np.linspace(0, T2, T2) domain2 = np.tile(domain.reshape(T2, 1), (1, S)) ending_b = b_splus1_ss initial_b = bvec1 guesses_b = (-1 / (domain2 + 1)) * (ending_b - initial_b) + ending_b ending_b_tail = np.tile(ending_b.reshape(1, S), (S, 1)) guesses_b = np.append(guesses_b, ending_b_tail, axis=0) domain3 = np.tile(np.linspace(0, 1, T2).reshape( T2, 1, ), (1, S)) initial_n = n_ss guesses_n = domain3 * (n_ss - initial_n) + initial_n ending_n_tail = np.tile(n_ss.reshape(1, S), (S, 1)) guesses_n = np.append(guesses_n, ending_n_tail, axis=0) guesses = (guesses_b, guesses_n) iter_TPI = int(0) dist = 10.0 r_params = (A, alpha, delta) w_params = (A, alpha) Y_params = (A, alpha) cnb_params = (S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bvec1, TPI_tol, diff) while (iter_TPI < maxiter) and (dist >= mindist): iter_TPI += 1 rpath = firms.get_r(r_params, Kpath_init, Lpath_init) wpath = firms.get_w(w_params, Kpath_init, Lpath_init) if hh_fsolve: ind = np.arange(S) bn_args = (rpath, wpath, ind, S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bvec1, TPI_tol, diff) npath, b_splus1_path, n_err_path, b_err_path = solve_bn_path( guesses, bn_args) bSp1_err_path = np.zeros((S, T2 + S - 1)) b_s_path = np.zeros((S, T2 + S - 1)) b_s_path[:, 0] = bvec1 b_s_path[1:, 1:] = b_splus1_path[:-1, :-1] cpath = hh.get_cons(rpath, wpath, b_s_path, b_splus1_path, npath) # update guesses for next iteration guesses = (np.transpose(b_splus1_path), np.transpose(npath)) else: cpath, npath, b_s_path, n_err_path, b_err_path, bSp1_err_path = \ get_cnbpath(cnb_params, rpath, wpath) b_splus1_path = np.append(b_s_path[1:, :T2], np.reshape(bSp1_err_path[-1, :], (1, T2)), axis=0) Kpath_new = np.zeros(T2 + S - 1) Kpath_new[:T2], Kpath_cnstr = aggr.get_K(b_s_path[:, :T2]) Kpath_new[T2:] = K_ss Kpath_cnstr = np.append(Kpath_cnstr, np.zeros(S - 1, dtype=bool)) Kpath_new[Kpath_cnstr] = 0.01 Lpath_new = np.zeros(T2 + S - 1) Lpath_new[:T2] = aggr.get_L(npath[:, :T2]) Lpath_new[T2:] = L_ss rpath_new = firms.get_r(r_params, Kpath_new, Lpath_new) wpath_new = firms.get_w(w_params, Kpath_new, Lpath_new) Ypath = aggr.get_Y(Y_params, Kpath_new, Lpath_new) Cpath = np.zeros(T2 + S - 1) Cpath[:T2] = aggr.get_C(cpath[:, :T2]) Cpath[T2:] = C_ss RCerrPath = (Ypath[:-1] - Cpath[:-1] - Kpath_new[1:] + (1 - delta) * Kpath_new[:-1]) # Check the distance of Kpath_new1 KL_path_new = np.append(Kpath_new[:T2], Lpath_new[:T2]) KL_path_init = np.append(Kpath_init[:T2], Lpath_init[:T2]) dist = ((KL_path_new - KL_path_init)**2).sum() # dist = np.absolute(KL_path_new - KL_path_init).max() print( 'TPI iter: ', iter_TPI, ', dist: ', "%10.4e" % (dist), ', max abs all errs: ', "%10.4e" % (np.absolute( np.hstack((b_err_path.max(axis=0), n_err_path.max(axis=0), bSp1_err_path.max(axis=0)))).max())) # The resource constraint does not bind across the transition # path until the equilibrium is solved Kpath_init = xi * Kpath_new + (1 - xi) * Kpath_init Lpath_init = xi * Lpath_new + (1 - xi) * Lpath_init if (iter_TPI == maxiter) and (dist > mindist): print('TPI reached maxiter and did not converge.') elif (iter_TPI == maxiter) and (dist <= mindist): print('TPI converged in the last iteration. ' + 'Should probably increase maxiter_TPI.') Kpath = Kpath_new Lpath = Lpath_new rpath = rpath_new wpath = wpath_new tpi_time = time.clock() - start_time tpi_output = { 'cpath': cpath, 'npath': npath, 'b_s_path': b_s_path, 'b_splus1_path': b_splus1_path, 'wpath': wpath, 'rpath': rpath, 'Kpath': Kpath, 'Lpath': Lpath, 'Ypath': Ypath, 'Cpath': Cpath, 'bSp1_err_path': bSp1_err_path, 'n_err_path': n_err_path, 'b_err_path': b_err_path, 'RCerrPath': RCerrPath, 'tpi_time': tpi_time } # Print maximum resource constraint error. Only look at resource # constraint up to period T2 - 1 because period T2 includes K_{t+1}, # which was forced to be the steady-state print('Max abs. RC error: ', "%10.4e" % (np.absolute(RCerrPath[:T2 - 1]).max())) # Print TPI computation time utils.print_time(tpi_time, 'TPI') if graphs: ''' ---------------------------------------------------------------- cur_path = string, path name of current directory output_fldr = string, folder in current path to save files output_dir = string, total path of images folder output_path = string, path of file name of figure to be saved tvec = (T2+S-1,) vector, time period vector tgridT = (T2,) vector, time period vector from 1 to T2 sgrid = (S,) vector, all ages from 1 to S tmat = (S, T2) matrix, time periods for decisions ages (S) and time periods (T2) smat = (S, T2) matrix, ages for all decisions ages (S) and time periods (T2) ---------------------------------------------------------------- ''' # Create directory if images directory does not already exist cur_path = os.path.split(os.path.abspath(__file__))[0] output_fldr = "images" output_dir = os.path.join(cur_path, output_fldr) if not os.access(output_dir, os.F_OK): os.makedirs(output_dir) # Plot time path of aggregate capital stock tvec = np.linspace(1, T2 + S - 1, T2 + S - 1) minorLocator = MultipleLocator(1) fig, ax = plt.subplots() plt.plot(tvec, Kpath, marker='D') # for the minor ticks, use no labels; default NullFormatter ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') plt.title('Time path for aggregate capital stock K') plt.xlabel(r'Period $t$') plt.ylabel(r'Aggregate capital $K_{t}$') output_path = os.path.join(output_dir, 'Kpath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of aggregate capital stock fig, ax = plt.subplots() plt.plot(tvec, Lpath, marker='D') # for the minor ticks, use no labels; default NullFormatter ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') plt.title('Time path for aggregate labor L') plt.xlabel(r'Period $t$') plt.ylabel(r'Aggregate labor $L_{t}$') output_path = os.path.join(output_dir, 'Lpath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of aggregate output (GDP) fig, ax = plt.subplots() plt.plot(tvec, Ypath, marker='D') # for the minor ticks, use no labels; default NullFormatter ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') plt.title('Time path for aggregate output (GDP) Y') plt.xlabel(r'Period $t$') plt.ylabel(r'Aggregate output $Y_{t}$') output_path = os.path.join(output_dir, 'Ypath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of aggregate consumption fig, ax = plt.subplots() plt.plot(tvec, Cpath, marker='D') # for the minor ticks, use no labels; default NullFormatter ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') plt.title('Time path for aggregate consumption C') plt.xlabel(r'Period $t$') plt.ylabel(r'Aggregate consumption $C_{t}$') output_path = os.path.join(output_dir, 'C_aggr_path') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of real wage fig, ax = plt.subplots() plt.plot(tvec, wpath, marker='D') # for the minor ticks, use no labels; default NullFormatter ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') plt.title('Time path for real wage w') plt.xlabel(r'Period $t$') plt.ylabel(r'Real wage $w_{t}$') output_path = os.path.join(output_dir, 'wpath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of real interest rate fig, ax = plt.subplots() plt.plot(tvec, rpath, marker='D') # for the minor ticks, use no labels; default NullFormatter ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') plt.title('Time path for real interest rate r') plt.xlabel(r'Period $t$') plt.ylabel(r'Real interest rate $r_{t}$') output_path = os.path.join(output_dir, 'rpath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of individual consumption distribution tgridT = np.linspace(1, T2, T2) sgrid = np.linspace(1, S, S) tmat, smat = np.meshgrid(tgridT, sgrid) cmap_c = cm.get_cmap('summer') fig = plt.figure() ax = fig.gca(projection='3d') ax.set_xlabel(r'period-$t$') ax.set_ylabel(r'age-$s$') ax.set_zlabel(r'individual consumption $c_{s,t}$') strideval = max(int(1), int(round(S / 10))) ax.plot_surface(tmat, smat, cpath[:, :T2], rstride=strideval, cstride=strideval, cmap=cmap_c) output_path = os.path.join(output_dir, 'cpath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of individual labor supply distribution cmap_n = cm.get_cmap('summer') fig = plt.figure() ax = fig.gca(projection='3d') ax.set_xlabel(r'period-$t$') ax.set_ylabel(r'age-$s$') ax.set_zlabel(r'individual labor supply $n_{s,t}$') strideval = max(int(1), int(round(S / 10))) ax.plot_surface(tmat, smat, npath[:, :T2], rstride=strideval, cstride=strideval, cmap=cmap_n) output_path = os.path.join(output_dir, 'npath') plt.savefig(output_path) # plt.show() plt.close() # Plot time path of individual savings distribution cmap_b = cm.get_cmap('summer') fig = plt.figure() ax = fig.gca(projection='3d') ax.set_xlabel(r'period-$t$') ax.set_ylabel(r'age-$s$') ax.set_zlabel(r'individual savings $b_{s,t}$') strideval = max(int(1), int(round(S / 10))) ax.plot_surface(tmat, smat, b_splus1_path[:, :T2], rstride=strideval, cstride=strideval, cmap=cmap_b) output_path = os.path.join(output_dir, 'bpath') plt.savefig(output_path) # plt.show() plt.close() return tpi_output
def twist_doughnut(guesses, td_args): ''' Parameters: guesses = distribution of capital and labor (various length list) w = wage rate ((T+S)x1 array) r = rental rate ((T+S)x1 array) BQ = aggregate bequests ((T+S)x1 array) T_H = lump sum tax over time ((T+S)x1 array) factor = scaling factor (scalar) j = which ability type is being solved for (scalar) s = which upper triangle loop is being solved for (scalar) t = which diagonal is being solved for (scalar) params = list of parameters (list) theta = replacement rates (Jx1 array) tau_bq = bequest tax rate (Jx1 array) rho = mortalit rate (Sx1 array) lambdas = ability weights (Jx1 array) e = ability type (SxJ array) initial_b = capital stock distribution in period 0 (SxJ array) chi_b = chi^b_j (Jx1 array) chi_n = chi^n_s (Sx1 array) Output: Value of Euler error (various length list) ''' rpath, wpath, s, t, S, T2, beta, sigma, l_tilde, b_ellip, upsilon, \ chi_n_vec, initial_b, diff = td_args length = len(guesses) // 2 b_guess = np.array(guesses[:length]) n_guess = np.array(guesses[length:]) b_guess[-1] = 0.0 # save nothing in last period if length == S: b_s = np.array([0] + list(b_guess[:-1])) else: # b_s = np.array([(initial_b[-(s + 3)])] + list(b_guess[:-1])) b_s = np.array([(initial_b[-(s + 2)])] + list(b_guess[:-1])) w = wpath[t:t + length] r = rpath[t:t + length] # Euler equations cons = hh.get_cons(r, w, b_s, b_guess, n_guess) b_params = (beta, sigma) euler_errors = hh.get_b_errors(b_params, r[1:], cons, diff) error1 = np.append(euler_errors, 0.0) n_args = (w, cons, sigma, l_tilde, chi_n_vec[-length:], b_ellip, upsilon, diff) error2 = hh.get_n_errors(n_guess, n_args) # Check and punish constraint violations mask1 = n_guess < 0 error2[mask1] += 1e14 mask2 = n_guess > l_tilde error2[mask2] += 1e14 mask3 = cons < 0 error2[mask3] += 1e14 return list(error1.flatten()) + list(error2.flatten())
def inner_loop(r, w, args): ''' -------------------------------------------------------------------- Given values for r and w, solve for the households' optimal decisions -------------------------------------------------------------------- INPUTS: r = scalar > 0, guess at steady-state interest rate w = scalar > 0, guess at steady-state wage args = length 14 tuple, (nvec_init, bvec_init, S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, EulDiff, SS_tol) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: euler_sys() aggr.get_K() aggr.get_L() firms.get_r() firms.get_w() OBJECTS CREATED WITHIN FUNCTION: nvec_init = (S,) vector, initial guesses at choice of labor supply bvec_init = (S,) vector, initial guesses at choice of savings S = integer >= 3, number of periods in individual lifetime beta = scalar in (0,1), discount factor sigma = scalar >= 1, coefficient of relative risk aversion l_tilde = scalar > 0, per-period time endowment for every agent b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], model-period depreciation rate of capital EulDiff = boolean, =True if simple difference Euler errors, otherwise percent deviation Euler errors SS_tol = scalar > 0, tolerance level for steady-state fsolve cvec = (S,) vector, lifetime consumption (c1, c2, ...cS) nvec = (S,) vector, lifetime labor supply (n1, n2, ...nS) bvec = (S,) vector, lifetime savings (b1, b2, ...bS) with b1=0 b_Sp1 = scalar, final period savings, should be close to zero n_errors = (S,) vector, labor supply Euler errors b_errors = (S-1,) vector, savings Euler errors K = scalar > 0, aggregate capital stock K_cstr = boolean, =True if K < epsilon L = scalar > 0, aggregate labor L_cstr = boolean, =True if L < epsilon r_params = length 3 tuple, (A, alpha, delta) w_params = length 2 tuple, (A, alpha) r_new = scalar > 0, guess at steady-state interest rate w_new = scalar > 0, guess at steady-state wage FILES CREATED BY THIS FUNCTION: None RETURNS: K, L, cvec, nvec, bvec, b_Sp1, r_new, w_new, n_errors, b_errors -------------------------------------------------------------------- ''' (nmat_init, bmat_init, S, J, beta, sigma, emat, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, lambdas, EulDiff, SS_tol) = args nmat = np.zeros((S, J)) bmat = np.zeros((S - 1, J)) n_err_mat = np.zeros((S, J)) b_err_mat = np.zeros((S - 1, J)) for j in range(J): euler_args = (r, w, beta, sigma, emat[:, j], l_tilde, chi_n_vec, b_ellip, upsilon, EulDiff, S, SS_tol) guesses = np.append(nmat_init[:, j], bmat_init[:, j]) results_euler = opt.root(euler_sys, guesses, args=(euler_args), method='lm', tol=SS_tol) nmat[:, j] = results_euler.x[:S] bmat[:, j] = results_euler.x[S:] n_err_mat[:, j] = results_euler.fun[:S] b_err_mat[:, j] = results_euler.fun[S:] b_s_mat = np.append(np.zeros((1, J)), bmat, axis=0) b_sp1_mat = np.append(bmat, np.zeros((1, J)), axis=0) cmat = hh.get_cons(r, w, b_s_mat, b_sp1_mat, nmat, emat) b_Sp1_vec = np.zeros(J) K, K_cnstr = aggr.get_K(bmat, lambdas) L = aggr.get_L(nmat, emat, lambdas) r_params = (A, alpha, delta) r_new = firms.get_r(K, L, r_params) w_params = (A, alpha, delta) w_new = firms.get_w_from_r(r_new, w_params) return (K, L, cmat, nmat, bmat, b_Sp1_vec, r_new, w_new, n_err_mat, b_err_mat)
def inner_loop(rpath, wpath, args): ''' -------------------------------------------------------------------- Given time paths for interest rates and wages, this function generates matrices for the time path of the distribution of individual consumption, labor supply, savings, the corresponding Euler errors for the labor supply decision and the savings decision, and the residual error of end-of-life savings associated with solving each lifetime decision. -------------------------------------------------------------------- INPUTS: rpath = (T2+S-1,) vector, equilibrium time path of interest rate wpath = (T2+S-1,) vector, equilibrium time path of the real wage args = length 12 tuple, (S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bvec1, n_ss, In_Tol, diff) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: hh.get_n_errors() hh.get_b_errors() OBJECTS CREATED WITHIN FUNCTION: S = integer in [3,80], number of periods an individual lives T2 = integer > S, number of periods until steady state beta = scalar in (0,1), discount factor sigma = scalar > 0, coefficient of relative risk aversion l_tilde = scalar > 0, time endowment for each agent each period b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s bvec1 = (S,) vector, initial period savings distribution In _tol = scalar > 0, tolerance level for fsolve's in TPI diff = boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 cpath = (S, T2+S-1) matrix, time path of the distribution of consumption npath = (S, T2+S-1) matrix, time path of the distribution of labor supply bpath = (S, T2+S-1) matrix, time path of the distribution of savings n_err_path = (S, T2+S-1) matrix, time path of distribution of labor supply Euler errors b_err_path = (S, T2+S-1) matrix, time path of distribution of savings Euler errors bSp1_err_path = (S, T2) matrix, residual last period savings, which should be close to zero in equilibrium. Nonzero elements of matrix should only be in first column and first row b_err_params = length 2 tuple, args to pass into hh.get_b_errors() p = integer in [1, S-1], index representing number of periods remaining in a lifetime, used to solve incomplete lifetimes cvec = (p,) vector, individual lifetime consumption decisions nvec = (p,) vector, individual lifetime labor supply decisions bvec = (p,) vector, individual lifetime savings decisions b_Sp1 = scalar, savings in last period for next period. Should be zero in equilibrium DiagMaskc = (p, p) boolean identity matrix DiagMaskb = (p-1, p-1) boolean identity matrix n_err_params = length 5 tuple, args to pass into hh.get_n_errors() n_err_vec = (p,) vector, individual lifetime labor supply Euler errors b_err_vec = (p-1,) vector, individual lifetime savings Euler errors t = integer in [0,T2-1], index of time period (minus 1) FILES CREATED BY THIS FUNCTION: None RETURNS: cpath, npath, bpath, n_err_path, b_err_path, bSp1_err_path -------------------------------------------------------------------- ''' (S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bvec1, n_ss, In_Tol, diff) = args cpath = np.zeros((S, T2 + S - 1)) npath = np.zeros((S, T2 + S - 1)) bpath = np.append(bvec1.reshape((S, 1)), np.zeros((S, T2 + S - 2)), axis=1) n_err_path = np.zeros((S, T2 + S - 1)) b_err_path = np.zeros((S, T2 + S - 1)) b_err_args = (beta, sigma, diff) # Solve the incomplete remaining lifetime decisions of agents alive # in period t=1 but not born in period t=1 for p in range(1, S): if p == 1: # p=1 individual only has an s=S labor supply decision n_S n_S1_init = n_ss[-1] nS1_args = (wpath[0], sigma, l_tilde, chi_n_vec[-1], b_ellip, upsilon, diff, rpath[0], bvec1[-1], 0.0) results_nS1 = opt.root(hh.get_n_errors, n_S1_init, args=(nS1_args), method='lm', tol=In_Tol) n_S1 = results_nS1.x npath[-1, 0] = n_S1 n_err_path[-1, 0] = results_nS1.fun cpath[-1, 0] = hh.get_cons(rpath[0], wpath[0], bvec1[-1], 0.0, n_S1) else: # 1<p<S chooses b_{s+1} and n_s and has incomplete lives DiagMaskb = np.eye(p - 1, dtype=bool) DiagMaskn = np.eye(p, dtype=bool) b_sp1_init = np.diag(bpath[S - p + 1:, :p - 1]) n_s_init = np.hstack( (n_ss[S - p], np.diag(npath[S - p + 1:, :p - 1]))) bn_init = np.hstack((b_sp1_init, n_s_init)) bn_args = (rpath[:p], wpath[:p], bvec1[-p], p, beta, sigma, l_tilde, chi_n_vec[-p:], b_ellip, upsilon, diff) results_bn = opt.root(hh.bn_errors, bn_init, args=(bn_args), tol=In_Tol) bvec = results_bn.x[:p - 1] nvec = results_bn.x[p - 1:] b_s_vec = np.append(bvec1[-p], bvec) b_sp1_vec = np.append(bvec, 0.0) cvec = hh.get_cons(rpath[:p], wpath[:p], b_s_vec, b_sp1_vec, nvec) npath[S - p:, :p] = DiagMaskn * nvec + npath[S - p:, :p] bpath[S - p + 1:, 1:p] = (DiagMaskb * bvec + bpath[S - p + 1:, 1:p]) cpath[S - p:, :p] = DiagMaskn * cvec + cpath[S - p:, :p] n_args = (wpath[:p], sigma, l_tilde, chi_n_vec[-p:], b_ellip, upsilon, diff, cvec) n_errors = hh.get_n_errors(nvec, *n_args) n_err_path[S - p:, :p] = (DiagMaskn * n_errors + n_err_path[S - p:, :p]) b_errors = hh.get_b_errors(cvec, rpath[1:p], *b_err_args) b_err_path[S - p + 1:, 1:p] = (DiagMaskb * b_errors + b_err_path[S - p + 1:, 1:p]) # Solve the complete remaining lifetime decisions of agents born # between period t=1 and t=T2 for t in range(T2): DiagMaskb = np.eye(S - 1, dtype=bool) DiagMaskn = np.eye(S, dtype=bool) b_sp1_init = np.diag(bpath[1:, t:t + S - 1]) if t == 0: n_s_init = np.hstack((n_ss[0], np.diag(npath[1:, t:t + S - 1]))) else: n_s_init = np.diag(npath[:, t - 1:t + S - 1]) bn_init = np.hstack((b_sp1_init, n_s_init)) bn_args = (rpath[t:t + S], wpath[t:t + S], 0.0, S, beta, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff) results_bn = opt.root(hh.bn_errors, bn_init, args=(bn_args), tol=In_Tol) bvec = results_bn.x[:S - 1] nvec = results_bn.x[S - 1:] b_s_vec = np.append(0.0, bvec) b_sp1_vec = np.append(bvec, 0.0) cvec = hh.get_cons(rpath[t:t + S], wpath[t:t + S], b_s_vec, b_sp1_vec, nvec) npath[:, t:t + S] = DiagMaskn * nvec + npath[:, t:t + S] bpath[1:, t + 1:t + S] = (DiagMaskb * bvec + bpath[1:, t + 1:t + S]) cpath[:, t:t + S] = DiagMaskn * cvec + cpath[:, t:t + S] n_args = (wpath[t:t + S], sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff, cvec) n_errors = hh.get_n_errors(nvec, *n_args) n_err_path[:, t:t + S] = (DiagMaskn * n_errors + n_err_path[:, t:t + S]) b_errors = hh.get_b_errors(cvec, rpath[t + 1:t + S], *b_err_args) b_err_path[1:, t + 1:t + S] = (DiagMaskb * b_errors + b_err_path[1:, t + 1:t + S]) return cpath, npath, bpath, n_err_path, b_err_path
def paths_life(params, beg_age, beg_wealth, nvec, rpath, wpath, b_init): ''' -------------------------------------------------------------------- Solve for the remaining lifetime savings decisions of an individual who enters the model at age beg_age, with corresponding initial wealth beg_wealth. Variable p is an integer in [2, S] representing the remaining periods of life. -------------------------------------------------------------------- INPUTS: params = length 5 tuple, (S, beta, sigma, TPI_tol, EulDiff) S = integer in [3,80], number of periods an individual lives beta = scalar in (0,1), discount factor for each model period sigma = scalar > 0, coefficient of relative risk aversion TPI_tol = scalar > 0, tolerance level for fsolve's in TPI EulDiff = Boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 beg_age = integer in [1,S-1], beginning age of remaining life beg_wealth = scalar, beginning wealth at beginning age nvec = (p,) vector, remaining exogenous labor supplies rpath = (p,) vector, remaining lifetime interest rates wpath = (p,) vector, remaining lifetime wages b_init = (p-1,) vector, initial guess for remaining lifetime savings OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: LfEulerSys() c6ssf.get_cvec() c6ssf.get_b_errors() OBJECTS CREATED WITHIN FUNCTION: p = integer in [2,S], remaining periods in life b_guess = (p-1,) vector, initial guess for lifetime savings decisions eullf_objs = length 7 tuple, (beta, sigma, beg_wealth, nvec, rpath, wpath, EulDiff) bpath = (p-1,) vector, optimal remaining lifetime savings decisions cpath = (p,) vector, optimal remaining lifetime consumption decisions c_cnstr = (p,) boolean vector, =True if c_p <= 0, b_err_params = length 2 tuple, (beta, sigma) b_err_vec = (p-1,) vector, Euler errors associated with optimal savings decisions FILES CREATED BY THIS FUNCTION: None RETURNS: bpath, cpath, b_err_vec -------------------------------------------------------------------- ''' S, beta, sigma, TPI_tol, EulDiff = params p = int(S - beg_age + 1) if beg_age == 1 and beg_wealth != 0: sys.exit("Beginning wealth is nonzero for age s=1.") if len(rpath) != p: sys.exit("Beginning age and length of rpath do not match.") if len(wpath) != p: sys.exit("Beginning age and length of wpath do not match.") if len(nvec) != p: sys.exit("Beginning age and length of nvec do not match.") b_guess = 1.01 * b_init eullf_objs = (beta, sigma, beg_wealth, nvec, rpath, wpath, EulDiff) results_bp = opt.root(LfEulerSys, b_guess, args=(eullf_objs), tol=TPI_tol) bpath = results_bp.x c_args = (nvec, rpath, wpath) cpath = hh.get_cons(bpath, beg_wealth, c_args) b_err_vec = results_bp.fun return bpath, cpath, b_err_vec
def inner_loop(r, w, Y, x, params): ''' -------------------------------------------------------------------- Given values for r and w, solve for equilibrium errors from the two first order conditions of the firm -------------------------------------------------------------------- INPUTS: r = scalar > 0, guess at steady-state interest rate w = scalar > 0, guess at steady-state wage Y = scalar > 0, guess steady-state output x = scalar > 0, guess as steady-state transfers per household params = length 16 tuple, (c1_init, S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, tax_params, fiscal_params, diff, hh_fsolve, SS_tol) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: hh.bn_solve() hh.c1_bSp1err() hh.get_cnb_vecs() aggr.get_K() aggr.get_L() firms.get_r() firms.get_w() OBJECTS CREATED WITHIN FUNCTION: c1_init = scalar > 0, initial guess for c1 S = integer >= 3, number of periods in individual lifetime beta = scalar in (0,1), discount factor sigma = scalar >= 1, coefficient of relative risk aversion l_tilde = scalar > 0, per-period time endowment for every agent b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], model-period depreciation rate of capital tax_params = length 3 tuple, (tau_l, tau_k, tau_c) fiscal_params = length 7 tuple, (tG1, tG2, alpha_X, alpha_G, rho_G, alpha_D, alpha_D0) diff = boolean, =True if simple difference Euler errors, otherwise percent deviation Euler errors hh_fsolve = boolean, =True if solve inner-loop household problem by choosing c_1 to set final period savings b_{S+1}=0. Otherwise, solve the household problem as multivariate root finder with 2S-1 unknowns and equations SS_tol = scalar > 0, tolerance level for steady-state fsolve tau_l = scalar, marginal tax rate on labor income tau_k = scalar, marginal tax rate on capital income tau_c = scalar, marginal tax rate on corporate income tG1 = integer, model period when budget closure rule begins tG2 = integer, model period when budget is closed alpha_X = scalar, ratio of lump sum transfers to GDP alpha_G = scalar, ratio of government spending to GDP prior to budget closure rule beginning rho_G = scalar in (0,1), rate of convergence to SS budget alpha_D = scalar, steady-state debt to GDP ratio alpha_D0 = scalar, debt to GDP ratio in the initial period r_params = length 3 tuple, args to pass into get_r() w_params = length 2 tuple, args to pass into get_w() Y_params = length 2 tuple, args to pass into get_Y() b_init = (S-1,) vector, initial guess at distribution of savings n_init = (S,) vector, initial guess at distribution of labor supply guesses = (2S-1,) vector, initial guesses at b and n bn_params = length 12 tuple, parameters to pass to solve_bn_path() (r, w, x, S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, tax_params, diff) euler_errors = (2S-1,) vector, Euler errors for FOCs for b and n b_splus1_vec = (S,) vector, optimal savings choice nvec = (S,) vector, optimal labor supply choice b_Sp1 = scalar, last period savings b_s_vec = (S,) vector, wealth enter period with cvec = (S,) vector, optimal consumption rpath = (S,) vector, lifetime path of interest rates wpath = (S,) vector, lifetime path of wages c1_args = length 10 tuple, args to pass into c1_bSp1err() c1_options = length 1 dict, options for c1_bSp1err() results_c1 = results object, results from c1_bSp1err() c1 = scalar > 0, optimal initial period consumption given r and w cnb_args = length 8 tuple, args to pass into get_cnb_vecs() cvec = (S,) vector, lifetime consumption (c1, c2, ...cS) nvec = (S,) vector, lifetime labor supply (n1, n2, ...nS) bvec = (S,) vector, lifetime savings (b1, b2, ...bS) with b1=0 b_Sp1 = scalar, final period savings, should be close to zero B = scalar > 0, aggregate savings B_cnstr = boolean, =True if B < 0 L = scalar > 0, aggregate labor debt = scalar, total government debt K = scalar > 0, aggregate capital stock K_cnstr = boolean, =True if K < 0 r_new = scalar > 0, implied steady-state interest rate w_new = scalar > 0, implied steady-state wage Y_new = scalar >0, implied steady-state output x_new = scalar >=0, implied transfers per household FILES CREATED BY THIS FUNCTION: None RETURNS: B, K, L, Y_new, debt, cvec, nvec, b_s_vec, b_splus1_vec, b_Sp1, x_new, r_new, w_new -------------------------------------------------------------------- ''' c1_init, S, beta, sigma, l_tilde, b_ellip, upsilon,\ chi_n_vec, A, alpha, delta, tax_params, fiscal_params,\ diff, hh_fsolve, SS_tol = params tau_l, tau_k, tau_c = tax_params tG1, tG2, alpha_X, alpha_G, rho_G, alpha_D, alpha_D0 = fiscal_params r_params = (A, alpha, delta, tau_c) w_params = (A, alpha) Y_params = (A, alpha) if hh_fsolve: b_init = np.ones((S - 1, 1)) * 0.05 n_init = np.ones((S, 1)) * 0.4 guesses = np.append(b_init, n_init) bn_params = (r, w, x, S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, tax_params, diff) [solutions, infodict, ier, message] = \ opt.fsolve(hh.bn_solve, guesses, args=bn_params, xtol=SS_tol, full_output=True) euler_errors = infodict['fvec'] print('Max Euler errors: ', np.absolute(euler_errors).max()) b_splus1_vec = np.append(solutions[:S - 1], 0.0) nvec = solutions[S - 1:] b_Sp1 = 0.0 b_s_vec = np.append(0.0, b_splus1_vec[:-1]) cvec = hh.get_cons(r, w, b_s_vec, b_splus1_vec, nvec, x, tax_params) else: rpath = r * np.ones(S) wpath = w * np.ones(S) xpath = x * np.ones(S) c1_options = {'maxiter': 500} c1_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, tax_params, xpath, rpath, wpath, diff) results_c1 = \ opt.root(hh.c1_bSp1err, c1_init, args=(c1_args), method='lm', tol=SS_tol, options=(c1_options)) c1_new = results_c1.x cnb_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, tax_params, diff) cvec, nvec, b_s_vec, b_Sp1 = \ hh.get_cnb_vecs(c1_new, rpath, wpath, xpath, cnb_args) b_splus1_vec = np.append(b_s_vec[1:], b_Sp1) B, B_cnstr = aggr.get_K(b_s_vec) L = aggr.get_L(nvec) L = np.maximum(0.0001, L) debt = alpha_D * Y K = B - debt K_cnstr = K < 0 if K_cnstr: print('Aggregate capital constraint is violated K<=0 for ' + 'in the steady state.') r_new = firms.get_r(r_params, K, L) w_new = firms.get_w(w_params, K, L) Y_new = aggr.get_Y(Y_params, K, L) x_new = (alpha_X * Y_new) / S return B, K, L, Y_new, debt, cvec, nvec, b_s_vec, b_splus1_vec, \ b_Sp1, x_new, r_new, w_new
def get_cnbpath(rpath, wpath, args): ''' -------------------------------------------------------------------- Given time paths for interest rates and wages, this function generates matrices for the time path of the distribution of individual consumption, labor supply, savings, the corresponding Euler errors for the labor supply decision and the savings decision, and the residual error of end-of-life savings associated with solving each lifetime decision. -------------------------------------------------------------------- INPUTS: rpath = (T2+S-1,) vector, equilibrium time path of interest rate wpath = (T2+S-1,) vector, equilibrium time path of the real wage args = length 13 tuple, (J, S, T2, emat, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bmat1, n_ss, In_Tol) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: hh.c1_bSp1err() hh.get_cnb_vecs() hh.get_n_errors() hh.get_b_errors() OBJECTS CREATED WITHIN FUNCTION: J = S = integer in [3,80], number of periods an individual lives T2 = integer > S, number of periods until steady state emat = (S, J) matrix beta = scalar in (0,1), discount factor sigma = scalar > 0, coefficient of relative risk aversion l_tilde = scalar > 0, time endowment for each agent each period b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s bmat1 = (S, J) matrix, initial period household savings distribution In_Tol = scalar > 0, tolerance level for TPI inner-loop root finders cpath = (S, J, T2+S-1) array, time path of the distribution of household consumption npath = (S, J, T2+S-1) array, time path of the distribution of household labor supply bpath = (S, J, T2+S-1) array, time path of the distribution of household savings n_err_path = (S, J, T2+S-1) matrix, time path of household labor supply Euler errors b_err_path = (S, J, T2+S-1) matrix, time path of household savings Euler errors per_rmn = integer in [1, S-1], index representing number of periods remaining in a lifetime, used to solve incomplete lifetimes j_ind = integer in [0, J-1], index representing ability type n_S1_init = scalar in (0, l_tilde), initial guess for n_{j,S,1} nS1_args = length 10 tuple, args to pass in to hh.get_n_errors() results_nS1 = results object, results from opt.root(hh.get_n_errors,...) DiagMaskb = (per_rmn-1, per_rmn-1) boolean identity matrix DiagMaskn = (per_rmn, per_rmn) boolean identity matrix b_sp1_init = (per_rmn-1,) vector, initial guess for remaining lifetime savings for household j in period t n_s_init = (per_rmn,) vector, initial guess for remaining lifetime labor supply for household j in period t bn_init = (2*per_rmn - 1,) vector, initial guess for remaining lifetime savings and labor supply for household j in period t bn_args = length 11 tuple, arguments to pass into opt.root(hh.bn_errors,...) results_bn = results object, results from opt.root(hh.bn_errors,) bvec = (per_rmn-1,) vector, optimal savings given rpath and wpath nvec = (per_rmn,) vector, optimal labor supply given rpath and wpath b_errors = (per_rmn-1,) vector, savings Euler errors n_errors = (per_rmn,) vector, labor supply Euler errors b_s_vec = (per_rmn,) vector, remaining lifetime beginning of period wealth for household j in period t b_sp1_vec = (per_rmn,) vector, remaining lifetime savings for household j in period t cvec = (per_rmn,) vector, remaining lifetime consumption for household j in period t t = integer in [0, T2-1], index of time period FILES CREATED BY THIS FUNCTION: None RETURNS: cpath, npath, bpath, n_err_path, b_err_path, bSp1_err_path -------------------------------------------------------------------- ''' (J, S, T2, lambdas, emat, zeta_mat, omega_path, mort_rates, chi_n_vec, chi_b_vec, beta, sigma, l_tilde, b_ellip, upsilon, g_y, bmat1, n_ss, In_Tol) = args cpath = np.zeros((S, J, T2 + S - 1)) npath = np.zeros((S, J, T2 + S - 1)) bpath = np.zeros((S, J, T2 + S - 1)) bpath[:, :, 0] = bmat1 n_err_path = np.zeros((S, J, T2 + S - 1)) b_err_path = np.zeros((S, J, T2 + S - 1)) # Solve the incomplete remaining lifetime decisions of agents alive # in period t=1 but not born in period t=1 for per_rmn in range(1, S): for j_ind in range(J): if per_rmn == 1: # per_rmn=1 individual only has an s=S labor supply # decision n_S n_S1_init = n_ss[-1, j_ind] nS1_args = (wpath[0], emat[-1, j_ind], sigma, l_tilde, chi_n_vec[-1], b_ellip, upsilon, rpath[0], bmat1[-1, j_ind], 0.0) results_nS1 = opt.root(hh.get_n_errors, n_S1_init, args=(nS1_args), method='lm', tol=In_Tol) n_S1 = results_nS1.x npath[-1, j_ind, 0] = n_S1 n_err_path[-1, j_ind, 0] = results_nS1.fun cpath[-1, j_ind, 0] = \ hh.get_cons(rpath[0], wpath[0], emat[-1, j_ind], bmat1[-1, j_ind], 0.0, n_S1) else: # 1<p<S chooses b_{s+1} and n_s and has incomplete lives DiagMaskb = np.eye(per_rmn - 1, dtype=bool) DiagMaskn = np.eye(per_rmn, dtype=bool) b_sp1_init = np.diag(bpath[S - per_rmn + 1:, j_ind, :per_rmn - 1]) n_s_init = \ np.hstack((n_ss[S - per_rmn, j_ind], np.diag(npath[S - per_rmn + 1:, j_ind, :per_rmn - 1]))) bn_init = np.hstack((b_sp1_init, n_s_init)) bn_args = (rpath[:per_rmn], wpath[:per_rmn], bmat1[-per_rmn, j_ind], emat[-per_rmn:, j_ind], per_rmn, beta, sigma, l_tilde, chi_n_vec[-per_rmn:], b_ellip, upsilon) results_bn = opt.root(hh.bn_errors, bn_init, args=(bn_args), tol=In_Tol) bvec = results_bn.x[:per_rmn - 1] nvec = results_bn.x[per_rmn - 1:] b_errors = results_bn.fun[:per_rmn - 1] n_errors = results_bn.fun[per_rmn - 1:] b_s_vec = np.append(bmat1[-per_rmn, j_ind], bvec) b_sp1_vec = np.append(bvec, 0.0) cvec = hh.get_cons(rpath[:per_rmn], wpath[:per_rmn], emat[-per_rmn:, j_ind], b_s_vec, b_sp1_vec, nvec) npath[S - per_rmn:, j_ind, :per_rmn] = \ DiagMaskn * nvec + npath[S - per_rmn:, j_ind, :per_rmn] bpath[S - per_rmn + 1:, j_ind, 1:per_rmn] = \ (DiagMaskb * bvec + bpath[S - per_rmn + 1:, j_ind, 1:per_rmn]) cpath[S - per_rmn:, j_ind, :per_rmn] = \ DiagMaskn * cvec + cpath[S - per_rmn:, j_ind, :per_rmn] n_err_path[S - per_rmn:, j_ind, :per_rmn] = \ (DiagMaskn * n_errors + n_err_path[S - per_rmn:, j_ind, :per_rmn]) b_err_path[S - per_rmn + 1:, j_ind, 1:per_rmn] = \ (DiagMaskb * b_errors + b_err_path[S - per_rmn + 1:, j_ind, 1:per_rmn]) # Solve the complete remaining lifetime decisions of agents born # between period t=1 and t=T2 for t in range(T2): for j_ind in range(J): DiagMaskb = np.eye(S - 1, dtype=bool) DiagMaskn = np.eye(S, dtype=bool) b_sp1_init = np.diag(bpath[1:, j_ind, t:t + S - 1]) if t == 0: n_s_init = np.hstack( (n_ss[0, j_ind], np.diag(npath[1:, j_ind, t:t + S - 1]))) else: n_s_init = np.diag(npath[:, j_ind, t - 1:t + S - 1]) bn_init = np.hstack((b_sp1_init, n_s_init)) bn_args = (rpath[t:t + S], wpath[t:t + S], 0.0, emat[:, j_ind], S, beta, sigma, l_tilde, chi_n_vec, b_ellip, upsilon) results_bn = opt.root(hh.bn_errors, bn_init, args=(bn_args), tol=In_Tol) bvec = results_bn.x[:S - 1] nvec = results_bn.x[S - 1:] b_errors = results_bn.fun[:S - 1] n_errors = results_bn.fun[S - 1:] b_s_vec = np.append(0.0, bvec) b_sp1_vec = np.append(bvec, 0.0) cvec = hh.get_cons(rpath[t:t + S], wpath[t:t + S], emat[:, j_ind], b_s_vec, b_sp1_vec, nvec) npath[:, j_ind, t:t + S] = (DiagMaskn * nvec + npath[:, j_ind, t:t + S]) bpath[1:, j_ind, t + 1:t + S] = \ DiagMaskb * bvec + bpath[1:, j_ind, t + 1:t + S] cpath[:, j_ind, t:t + S] = (DiagMaskn * cvec + cpath[:, j_ind, t:t + S]) n_err_path[:, j_ind, t:t + S] = \ DiagMaskn * n_errors + n_err_path[:, j_ind, t:t + S] b_err_path[1:, j_ind, t + 1:t + S] = \ DiagMaskb * b_errors + b_err_path[1:, j_ind, t + 1:t + S] return cpath, npath, bpath, n_err_path, b_err_path
def get_SS(init_vals, args, graphs=False): ''' -------------------------------------------------------------------- Solve for the steady-state solution of the S-period-lived agent OG model with endogenous labor supply and a small open economy. -------------------------------------------------------------------- INPUTS: init_vals = length 5 tuple, (Kss_init, Lss_init, rss_init, wss_init,c1_init) args = length 14 tuple, (S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, SS_tol, EulDiff, hh_fsolve, KL_outer) graphs = boolean, =True if output steady-state graphs OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: firms.get_r() firms.get_w() hh.bn_solve() hh.c1_bSp1err() hh.get_cnb_vecs() aggr.get_K() aggr.get_L() aggr.get_Y() aggr.get_C() hh.get_cons() hh.get_n_errors() hh.get_b_errors() utils.print_time() OBJECTS CREATED WITHIN FUNCTION: start_time = scalar > 0, clock time at beginning of program Kss_init = scalar > 0, initial guess for steady-state aggregate capital stock supplied Lss_init = scalar > 0, initial guess for steady-state aggregate labor rss_init = scalar > 0, initial guess for steady-state interest rate wss_init = scalar > 0, initial guess for steady-state wage c1_init = scalar > 0, initial guess for first period consumpt'n S = integer in [3, 80], number of periods an individual lives beta = scalar in (0,1), discount factor for each model per sigma = scalar > 0, coefficient of relative risk aversion l_tilde = scalar > 0, time endowment for each agent each period b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], model-period depreciation rate of capital SS_tol = scalar > 0, tolerance level for steady-state fsolve EulDiff = Boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 maxiter_SS = integer >= 1, maximum number of iterations in outer loop bisection method iter_SS = integer >= 0, index of iteration number mindist_SS = scalar > 0, minimum distance tolerance for convergence dist_SS = scalar > 0, distance metric for current iteration xi_SS = scalar in (0,1], updating parameter KL_init = (2,) vector, (K_init, L_init) c1_options = length 1 dict, options to pass into opt.root(c1_bSp1err,...) cnb_args = length 8 tuple, args to pass into get_cnb_vecs() #r_params = length 3 tuple, args to pass into get_r() w_params = length 2 tuple, args to pass into get_w() K_init = scalar, initial value of aggregate capital stock supplied L_init = scalar, initial value of aggregate labor r_init = scalar, initial value for interest rate w_init = scalar, initial value for wage K_d = scalar, capital demand rpath = (S,) vector, lifetime path of interest rates wpath = (S,) vector, lifetime path of wages c1_args = length 10 tuple, args to pass into c1_bSp1err() results_c1 = results object, root finder results from opt.root(c1_bSp1err,...) c1_new = scalar, updated value of optimal c1 given r_init and w_init cvec_new = (S,) vector, updated values for lifetime consumption nvec_new = (S,) vector, updated values for lifetime labor supply b_s_new = (S,) vector, updated values for lifetime wealth b_splus1_new = (S,) vector, updated values for lifetime savings (b1, b2,...bS) b_Sp1_new = scalar, updated value for savings in last period, should be arbitrarily close to zero K_new = scalar, updated K given bvec_new K_cnstr = boolean, =True if K_new <= 0 L_new = scalar, updated L given nvec_new KL_new = (2,) vector, updated K and L given bvec_new, nvec_new K_ss = scalar > 0, steady-state aggregate capital stock supplied K_d_ss = scalar > 0, steady-state aggregate capital stock demanded L_ss = scalar > 0, steady-state aggregate labor r_ss = scalar > 0, steady-state interest rate w_ss = scalar > 0, steady-state wage c1_ss = scalar > 0, steady-state consumption in first period c_ss = (S,) vector, steady-state lifetime consumption n_ss = (S,) vector, steady-state lifetime labor supply b_s_ss = (S,) vector, steady-state wealth enter period with b_splus1_ss = (S,) vector, steady-state lifetime savings (b1_ss, b2_ss, ...bS_ss) where b1_ss=0 b_Sp1_ss = scalar, steady-state savings for period after last period of life. b_Sp1_ss approx. 0 in equilibrium Y_params = length 2 tuple, (A, alpha) Y_ss = scalar > 0, steady-state aggregate output (GDP) C_ss = scalar > 0, steady-state aggregate consumption n_err_params = length 5 tuple, args to pass into get_n_errors() n_err_ss = (S,) vector, lifetime labor supply Euler errors b_err_params = length 2 tuple, args to pass into get_b_errors() b_err_ss = (S-1) vector, lifetime savings Euler errors RCerr_ss = scalar, resource constraint error ss_time = scalar, seconds elapsed to run steady-state comput'n ss_output = length 14 dict, steady-state objects {n_ss, b_ss, c_ss, b_Sp1_ss, w_ss, r_ss, K_ss, L_ss, Y_ss, C_ss, n_err_ss, b_err_ss, RCerr_ss, ss_time} FILES CREATED BY THIS FUNCTION: SS_bc.png SS_n.png RETURNS: ss_output -------------------------------------------------------------------- ''' start_time = time.clock() c1_init = init_vals (S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, r_star, SS_tol, EulDiff, hh_fsolve) = args c1_options = {'maxiter': 500} cnb_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, EulDiff) w_params = (A, alpha, delta) K_params = (A, alpha, delta) r_ss = r_star w_ss = firms.get_w(r_ss, w_params) if hh_fsolve: b_init = np.ones((S - 1, 1)) * 0.05 n_init = np.ones((S, 1)) * 0.4 guesses = np.append(b_init, n_init) bn_params = (r_ss, w_ss, S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, EulDiff) [solutions, infodict, ier, message] = \ opt.fsolve(hh.bn_solve, guesses, args=bn_params, xtol=SS_tol, full_output=True) euler_errors = infodict['fvec'] print('Max Euler errors: ', np.absolute(euler_errors).max()) b_splus1_ss = np.append(solutions[:S - 1], 0.0) n_ss = solutions[S - 1:] b_Sp1_ss = 0.0 b_s_ss = np.append(0.0, b_splus1_ss[:-1]) c_ss = hh.get_cons(r_ss, w_ss, b_s_ss, b_splus1_ss, n_ss) else: rpath = r_ss * np.ones(S) wpath = w_ss * np.ones(S) c1_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, rpath, wpath, EulDiff) c1_options = {'maxiter': 500} results_c1 = \ opt.root(hh.c1_bSp1err, c1_new, args=(c1_args), method='lm', tol=SS_tol, options=(c1_options)) c1_ss = results_c1.x cnb_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, EulDiff) c_ss, n_ss, b_s_ss, b_Sp1_ss = hh.get_cnb_vecs(c1_ss, rpath, wpath, cnb_args) b_splus1_ss = np.append(b_s_ss[1:], b_Sp1_ss) L_ss = aggr.get_L_s(n_ss) K_s_ss, K_cnstr = aggr.get_K_s(b_s_ss) K_d_ss = firms.get_K_d(r_ss, L_ss, K_params) Y_params = (A, alpha) Y_ss = aggr.get_Y(Y_params, K_d_ss, L_ss) C_ss = aggr.get_C(c_ss) n_err_args = (w_ss, c_ss, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, EulDiff) n_err_ss = hh.get_n_errors(n_ss, n_err_args) b_err_params = (beta, sigma) b_err_ss = hh.get_b_errors(b_err_params, r_ss, c_ss, EulDiff) NX_ss = Y_ss - C_ss - delta * K_s_ss RCerr_ss = Y_ss - C_ss - delta * K_s_ss - NX_ss ss_time = time.clock() - start_time ss_output = { 'n_ss': n_ss, 'b_s_ss': b_s_ss, 'b_splus1_ss': b_splus1_ss, 'c_ss': c_ss, 'b_Sp1_ss': b_Sp1_ss, 'w_ss': w_ss, 'r_ss': r_ss, 'K_s_ss': K_s_ss, 'K_d_ss': K_d_ss, 'L_ss': L_ss, 'Y_ss': Y_ss, 'C_ss': C_ss, 'n_err_ss': n_err_ss, 'b_err_ss': b_err_ss, 'RCerr_ss': RCerr_ss, 'ss_time': ss_time } print('n_ss is: ', n_ss) print('b_splus1_ss is: ', b_splus1_ss) print('K_s_ss=', K_s_ss, 'K_d_ss=', K_d_ss, ', L_ss=', L_ss) print('r_ss=', r_ss, ', w_ss=', w_ss) print('Maximum abs. labor supply Euler error is: ', np.absolute(n_err_ss).max()) print('Maximum abs. savings Euler error is: ', np.absolute(b_err_ss).max()) print('Resource constraint error is: ', RCerr_ss) print('Net Exports = ', NX_ss) print('Output and consumption: ', Y_ss, C_ss) print('Steady-state residual savings b_Sp1 is: ', b_Sp1_ss) # Print SS computation time utils.print_time(ss_time, 'SS') if graphs: ''' ---------------------------------------------------------------- cur_path = string, path name of current directory output_fldr = string, folder in current path to save files output_dir = string, total path of images folder output_path = string, path of file name of figure to be saved age_pers = (S,) vector, ages from 1 to S ---------------------------------------------------------------- ''' # Create directory if images directory does not already exist cur_path = os.path.split(os.path.abspath(__file__))[0] output_fldr = 'images' output_dir = os.path.join(cur_path, output_fldr) if not os.access(output_dir, os.F_OK): os.makedirs(output_dir) # Plot steady-state consumption and savings distributions age_pers = np.arange(1, S + 1) fig, ax = plt.subplots() plt.plot(age_pers, c_ss, marker='D', label='Consumption') plt.plot(age_pers, b_splus1_ss, marker='D', label='Savings') # for the minor ticks, use no labels; default NullFormatter minorLocator = MultipleLocator(1) ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') # plt.title('Steady-state consumption and savings', fontsize=20) plt.xlabel(r'Age $s$') plt.ylabel(r'Units of consumption') plt.xlim((0, S + 1)) # plt.ylim((-1.0, 1.15 * (b_ss.max()))) plt.legend(loc='upper left') output_path = os.path.join(output_dir, 'SS_bc') plt.savefig(output_path) # plt.show() plt.close() # Plot steady-state labor supply distributions fig, ax = plt.subplots() plt.plot(age_pers, n_ss, marker='D', label='Labor supply') # for the minor ticks, use no labels; default NullFormatter minorLocator = MultipleLocator(1) ax.xaxis.set_minor_locator(minorLocator) plt.grid(b=True, which='major', color='0.65', linestyle='-') # plt.title('Steady-state labor supply', fontsize=20) plt.xlabel(r'Age $s$') plt.ylabel(r'Labor supply') plt.xlim((0, S + 1)) # plt.ylim((-0.1, 1.15 * (n_ss.max()))) plt.legend(loc='upper right') output_path = os.path.join(output_dir, 'SS_n') plt.savefig(output_path) # plt.show() plt.close() return ss_output
def feasible(bvec, params): ''' -------------------------------------------------------------------- Check whether a vector of steady-state savings is feasible in that it satisfies the nonnegativity constraints on consumption in every period c_s > 0 and that the aggregate capital stock is strictly positive K > 0 -------------------------------------------------------------------- INPUTS: bvec = (S-1,) vector, household savings b_{s+1} params = length 4 tuple, (nvec, A, alpha, delta) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: get_L() get_K() get_w() get_r() get_cvec() OBJECTS CREATED WITHIN FUNCTION: nvec = (S,) vector, exogenous labor supply values n_s A = scalar > 0, total factor productivity alpha = scalar in (0, 1), capital share of income delta = scalar in (0, 1), per-period depreciation rate S = integer >= 3, number of periods in individual life L = scalar > 0, aggregate labor K = scalar, steady-state aggregate capital stock K_cstr = boolean, =True if K <= 0 w_params = length 2 tuple, (A, alpha) w = scalar, steady-state wage r_params = length 3 tuple, (A, alpha, delta) r = scalar, steady-state interest rate bvec2 = (S,) vector, steady-state savings distribution plus initial period wealth of zero cvec = (S,) vector, steady-state consumption by age c_cnstr = (S,) Boolean vector, =True for elements for which c_s<=0 b_cnstr = (S-1,) Boolean, =True for elements for which b_s causes a violation of the nonnegative consumption constraint FILES CREATED BY THIS FUNCTION: None RETURNS: b_cnstr, c_cnstr, K_cnstr -------------------------------------------------------------------- ''' nvec, A, alpha, delta = params S = nvec.shape[0] L = aggr.get_L(nvec) K, K_cstr = aggr.get_K(bvec) if not K_cstr: w_params = (A, alpha) w = firms.get_w(K, L, w_params) r_params = (A, alpha, delta) r = firms.get_r(K, L, r_params) c_params = (nvec, r, w) cvec = hh.get_cons(bvec, 0.0, c_params) c_cstr = cvec <= 0 b_cstr = c_cstr[:-1] + c_cstr[1:] else: c_cstr = np.ones(S, dtype=bool) b_cstr = np.ones(S - 1, dtype=bool) return c_cstr, K_cstr, b_cstr
def get_SS(bss_guess, args, graphs=False): ''' -------------------------------------------------------------------- Solve for the steady-state solution of the S-period-lived agent OG model with exogenous labor supply using one root finder in bvec -------------------------------------------------------------------- INPUTS: bss_guess = (S-1,) vector, initial guess for b_ss args = length 8 tuple, (nvec, beta, sigma, A, alpha, delta, SS_tol, SS_EulDiff) graphs = boolean, =True if output steady-state graphs OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: SS_EulErrs() aggr.get_K() aggr.get_L() aggr.get_Y() aggr.get_C() firms.get_r() firms.get_w() hh.get_cons() utils.print_time() get_ss_graphs() OBJECTS CREATED WITHIN FUNCTION: start_time = scalar > 0, clock time at beginning of program nvec = (S,) vector, exogenous lifetime labor supply n_s beta = scalar in (0,1), discount factor for each model per sigma = scalar > 0, coefficient of relative risk aversion A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], model-period depreciation rate of capital SS_tol = scalar > 0, tolerance level for steady-state fsolve SS_EulDiff = Boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 b_args = length 7 tuple, args passed to opt.root(SS_EulErrs,...) results_b = results object, output from opt.root(SS_EulErrs,...) b_ss = (S-1,) vector, steady-state savings b_{s+1} K_ss = scalar > 0, steady-state aggregate capital stock Kss_cstr = boolean, =True if K_ss < epsilon L = scalar > 0, exogenous aggregate labor r_params = length 3 tuple, (A, alpha, delta) r_ss = scalar > 0, steady-state interest rate w_params = length 2 tuple, (A, alpha) w_ss = scalar > 0, steady-state wage c_args = length 3 tuple, (nvec, r_ss, w_ss) c_ss = (S,) vector, steady-state individual consumption c_s Y_params = length 2 tuple, (A, alpha) Y_ss = scalar > 0, steady-state aggregate output (GDP) C_ss = scalar > 0, steady-state aggregate consumption b_err_ss = (S-1,) vector, Euler errors associated with b_ss RCerr_ss = scalar, steady-state resource constraint error ss_time = scalar > 0, time elapsed during SS computation (in seconds) ss_output = length 10 dict, steady-state objects {b_ss, c_ss, w_ss, r_ss, K_ss, Y_ss, C_ss, b_err_ss, RCerr_ss, ss_time} FILES CREATED BY THIS FUNCTION: None RETURNS: ss_output -------------------------------------------------------------------- ''' start_time = time.clock() nvec, beta, sigma, A, alpha, delta, SS_tol, SS_EulDiff = args b_args = (nvec, beta, sigma, A, alpha, delta, SS_EulDiff) results_b = opt.root(SS_EulErrs, bss_guess, args=(b_args)) b_ss = results_b.x K_ss, Kss_cstr = aggr.get_K(b_ss) L = aggr.get_L(nvec) r_params = (A, alpha, delta) r_ss = firms.get_r(K_ss, L, r_params) w_params = (A, alpha) w_ss = firms.get_w(K_ss, L, w_params) c_args = (nvec, r_ss, w_ss) c_ss = hh.get_cons(b_ss, 0.0, c_args) Y_params = (A, alpha) Y_ss = aggr.get_Y(K_ss, L, Y_params) C_ss = aggr.get_C(c_ss) b_err_ss = results_b.fun RCerr_ss = Y_ss - C_ss - delta * K_ss ss_time = time.clock() - start_time ss_output = { 'b_ss': b_ss, 'c_ss': c_ss, 'w_ss': w_ss, 'r_ss': r_ss, 'K_ss': K_ss, 'Y_ss': Y_ss, 'C_ss': C_ss, 'b_err_ss': b_err_ss, 'RCerr_ss': RCerr_ss, 'ss_time': ss_time } print('b_ss is: ', b_ss) print('K_ss=', K_ss, ', r_ss=', r_ss, ', w_ss=', w_ss) print('Max. abs. savings Euler error is: ', np.absolute(b_err_ss).max()) print('Max. abs. resource constraint error is: ', np.absolute(RCerr_ss).max()) # Print SS computation time utils.print_time(ss_time, 'SS') if graphs: get_ss_graphs(c_ss, b_ss) return ss_output
def get_cnbpath(rpath, wpath, BQpath, args): ''' -------------------------------------------------------------------- Given time paths for interest rates and wages, this function generates matrices for the time path of the distribution of individual consumption, labor supply, savings, the corresponding Euler errors for the labor supply decision and the savings decision, and the residual error of end-of-life savings associated with solving each lifetime decision. -------------------------------------------------------------------- INPUTS: rpath = (T2+S-1,) vector, equilibrium time path of interest rate wpath = (T2+S-1,) vector, equilibrium time path of the real wage args = length 13 tuple, (J, S, T2, emat, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bmat1, n_ss, In_Tol) OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: hh.c1_bSp1err() hh.get_cnb_vecs() hh.get_n_errors() hh.get_b_errors() OBJECTS CREATED WITHIN FUNCTION: J = S = integer in [3,80], number of periods an individual lives T2 = integer > S, number of periods until steady state emat = (S, J) matrix beta = scalar in (0,1), discount factor sigma = scalar > 0, coefficient of relative risk aversion l_tilde = scalar > 0, time endowment for each agent each period b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s bmat1 = (S, J) matrix, initial period household savings distribution In_Tol = scalar > 0, tolerance level for TPI inner-loop root finders cpath = (S, J, T2+S-1) array, time path of the distribution of household consumption npath = (S, J, T2+S-1) array, time path of the distribution of household labor supply bpath = (S, J, T2+S-1) array, time path of the distribution of household savings n_err_path = (S, J, T2+S-1) matrix, time path of household labor supply Euler errors b_err_path = (S, J, T2+S-1) matrix, time path of household savings Euler errors per_rmn = integer in [1, S-1], index representing number of periods remaining in a lifetime, used to solve incomplete lifetimes j_ind = integer in [0, J-1], index representing ability type n_S1_init = scalar in (0, l_tilde), initial guess for n_{j,S,1} nS1_args = length 10 tuple, args to pass in to hh.get_n_errors() results_nS1 = results object, results from opt.root(hh.get_n_errors,...) DiagMaskb = (per_rmn-1, per_rmn-1) boolean identity matrix DiagMaskn = (per_rmn, per_rmn) boolean identity matrix b_sp1_init = (per_rmn-1,) vector, initial guess for remaining lifetime savings for household j in period t n_s_init = (per_rmn,) vector, initial guess for remaining lifetime labor supply for household j in period t bn_init = (2*per_rmn - 1,) vector, initial guess for remaining lifetime savings and labor supply for household j in period t bn_args = length 11 tuple, arguments to pass into opt.root(hh.bn_errors,...) results_bn = results object, results from opt.root(hh.bn_errors,) bvec = (per_rmn-1,) vector, optimal savings given rpath and wpath nvec = (per_rmn,) vector, optimal labor supply given rpath and wpath b_errors = (per_rmn-1,) vector, savings Euler errors n_errors = (per_rmn,) vector, labor supply Euler errors b_s_vec = (per_rmn,) vector, remaining lifetime beginning of period wealth for household j in period t b_sp1_vec = (per_rmn,) vector, remaining lifetime savings for household j in period t cvec = (per_rmn,) vector, remaining lifetime consumption for household j in period t t = integer in [0, T2-1], index of time period FILES CREATED BY THIS FUNCTION: None RETURNS: cpath, npath, bpath, n_err_path, b_err_path, bSp1_err_path -------------------------------------------------------------------- ''' (J, S, T2, lambdas, emat, zeta_mat, omega_path, mort_rates, chi_n_vec, chi_b_vec, beta, sigma, l_tilde, b_ellip, upsilon, g_y, bmat1, n_ss, In_Tol) = args cpath = np.zeros((S, J, T2 + S - 1)) npath = np.zeros((S, J, T2 + S - 1)) bpath = np.zeros((S, J, T2 + S)) bpath[:, :, 0] = bmat1 n_err_path = np.zeros((S, J, T2 + S - 1)) b_err_path = np.zeros((S, J, T2 + S)) # Solve the incomplete remaining lifetime decisions of agents alive # in period t=1 but not born in period t=1 for per_rmn in range(1, S): for j_ind in range(J): if per_rmn == 1: # per_rmn=1 individual only has an s=S labor supply # decision n_S bn_S1_init = np.array([bmat1[-1, j_ind], n_ss[-1, j_ind]]) bnS1_args = \ (rpath[0], wpath[0], BQpath[0], bmat1[-2, j_ind], lambdas[j_ind], emat[-1, j_ind], zeta_mat[-1, j_ind], omega_path[0, -1], mort_rates[-1], per_rmn, beta, sigma, l_tilde, chi_n_vec[-1], chi_b_vec[j_ind], b_ellip, upsilon, g_y) results_bnS1 = opt.root(hh.bn_errors, bn_S1_init, args=(bnS1_args), method='lm', tol=In_Tol) b_S1, n_S1 = results_bnS1.x npath[-1, j_ind, 0] = n_S1 bpath[-1, j_ind, 1] = b_S1 b_err_path[-1, j_ind, 1], n_err_path[-1, j_ind, 0] = \ results_bnS1.fun c_args = (emat[-1, j_ind], zeta_mat[-1, j_ind], lambdas[j_ind], omega_path[0, -1], g_y) cpath[-1, j_ind, 0] = \ hh.get_cons(rpath[0], wpath[0], bmat1[-2, j_ind], b_S1, n_S1, BQpath[0], c_args) # print('per_rmn=', per_rmn, 'j=', j_ind, # ', Max Err=', "%10.4e" % # np.absolute(results_bnS1.fun).max()) # if np.absolute(results_bnS1.fun).max() > 1e-10: # print(results_bnS1.success) else: # 1<p<S chooses b_{s+1} and n_s and has incomplete lives DiagMaskbn = np.eye(per_rmn, dtype=bool) b_sp1_init = np.diag(bpath[-per_rmn:, j_ind, :per_rmn]) n_s_init = \ np.hstack((n_ss[S - per_rmn, j_ind], np.diag(npath[S - per_rmn + 1:, j_ind, :per_rmn - 1]))) bn_init = np.hstack((b_sp1_init, n_s_init)) bn_args = \ (rpath[:per_rmn], wpath[:per_rmn], BQpath[:per_rmn], bmat1[-(per_rmn + 1), j_ind], lambdas[j_ind], emat[-per_rmn:, j_ind], zeta_mat[-per_rmn, j_ind], np.diag(omega_path[:per_rmn, -per_rmn:]), mort_rates[-per_rmn:], per_rmn, beta, sigma, l_tilde, chi_n_vec[-per_rmn:], chi_b_vec[j_ind], b_ellip, upsilon, g_y) results_bn = opt.root(hh.bn_errors, bn_init, args=(bn_args), method='lm', tol=In_Tol) bvec = results_bn.x[:per_rmn] nvec = results_bn.x[per_rmn:] b_errors = results_bn.fun[:per_rmn] n_errors = results_bn.fun[per_rmn:] b_s_vec = np.append(bmat1[-(per_rmn + 1), j_ind], bvec[:-1]) b_sp1_vec = bvec.copy() c_args = (emat[-per_rmn:, j_ind], zeta_mat[-per_rmn:, j_ind], lambdas[j_ind], np.diag(omega_path[:per_rmn, -per_rmn:]), g_y) cvec = hh.get_cons(rpath[:per_rmn], wpath[:per_rmn], b_s_vec, b_sp1_vec, nvec, BQpath[:per_rmn], c_args) npath[-per_rmn:, j_ind, :per_rmn] = \ DiagMaskbn * nvec + npath[-per_rmn:, j_ind, :per_rmn] bpath[-per_rmn:, j_ind, 1:per_rmn + 1] = \ (DiagMaskbn * bvec + bpath[-per_rmn:, j_ind, 1:per_rmn + 1]) cpath[-per_rmn:, j_ind, :per_rmn] = \ DiagMaskbn * cvec + cpath[-per_rmn:, j_ind, :per_rmn] n_err_path[-per_rmn:, j_ind, :per_rmn] = \ (DiagMaskbn * n_errors + n_err_path[-per_rmn:, j_ind, :per_rmn]) b_err_path[-per_rmn:, j_ind, 1:per_rmn + 1] = \ (DiagMaskbn * b_errors + b_err_path[-per_rmn:, j_ind, 1:per_rmn + 1]) # print('per_rmn=', per_rmn, 'j=', j_ind, # ', Max Err=', "%10.4e" % # results_bn.fun.max()) # if np.absolute(results_bn.fun).max() > 1e-10: # print(results_bn.success) # Solve the complete remaining lifetime decisions of agents born # between period t=1 and t=T2 DiagMaskbn = np.eye(S, dtype=bool) # print('bpath, j_ind==0', bpath[:5, 0, :5]) # print('bpath, j_ind==2', bpath[:5, 2, :5]) # print('bpath, j_ind==4', bpath[:5, 4, :5]) # print('npath, j_ind==0', npath[:5, 0, :5]) # print('npath, j_ind==2', npath[:5, 2, :5]) # print('npath, j_ind==4', npath[:5, 4, :5]) # print('n_ss[0, 2], j_ind=2', n_ss[0, 2]) for t in range(T2): for j_ind in range(J): if t == 0: # In period t=0, make initial guess vectors a little # more feasible by decreasing bvec by 10% and increasing # nvec by 15% b_sp1_init = 0.9 * np.diag(bpath[:, j_ind, t:t + S]) n1_init = np.diag(npath[1:, j_ind, t:t + S - 1])[0] n2_init = np.diag(npath[1:, j_ind, t:t + S - 1])[1] n_s_init = 1.1 * np.append(2 * n1_init - n2_init, np.diag(npath[1:, j_ind, t:t + S - 1])) # if j_ind == 2: # print('bpath, j_ind=1', np.diag(bpath[:, 1, :S])) # print('npath, j_ind=1', np.diag(bpath[:, 1, 1:S + 1])) # print('t=', t, ', j=', j_ind, 'n_s_init=', n_s_init) # print('t=', t, ', j=', j_ind, 'b_sp1_init=', b_sp1_init) # n_s_init = np.append(n_ss[0, j_ind], # np.diag(npath[1:, j_ind, # t:t + S - 1])) else: b_sp1_init = np.diag(bpath[:, j_ind, t:t + S]) n_s_init = np.diag(npath[:, j_ind, t - 1:t + S - 1]) bn_init = np.hstack((b_sp1_init, n_s_init)) bn_args = (rpath[t:t + S], wpath[t:t + S], BQpath[t:t + S], 0.0, lambdas[j_ind], emat[:, j_ind], zeta_mat[:, j_ind], np.diag(omega_path[t:t + S, :]), mort_rates, S, beta, sigma, l_tilde, chi_n_vec, chi_b_vec[j_ind], b_ellip, upsilon, g_y) results_bn = opt.root(hh.bn_errors, bn_init, args=(bn_args), method='lm', tol=In_Tol) bvec = results_bn.x[:S] nvec = results_bn.x[S:] b_errors = results_bn.fun[:S] n_errors = results_bn.fun[S:] b_s_vec = np.append(0.0, bvec[:-1]) b_sp1_vec = bvec.copy() c_args = (emat[:, j_ind], zeta_mat[:, j_ind], lambdas[j_ind], np.diag(omega_path[t:t + S, :]), g_y) cvec = hh.get_cons(rpath[t:t + S], wpath[t:t + S], b_s_vec, b_sp1_vec, nvec, BQpath[t:t + S], c_args) # if t == 0 and j_ind == 2: # print('bvec=', bvec) # print('nvec=', nvec) # print('cvec=', cvec) # print('b_errors=', b_errors) # print('n_errors=', n_errors) npath[:, j_ind, t:t + S] = (DiagMaskbn * nvec + npath[:, j_ind, t:t + S]) bpath[:, j_ind, t + 1:t + S + 1] = \ DiagMaskbn * bvec + bpath[:, j_ind, t + 1:t + S + 1] cpath[:, j_ind, t:t + S] = (DiagMaskbn * cvec + cpath[:, j_ind, t:t + S]) n_err_path[:, j_ind, t:t + S] = \ DiagMaskbn * n_errors + n_err_path[:, j_ind, t:t + S] b_err_path[:, j_ind, t + 1:t + S + 1] = \ (DiagMaskbn * b_errors + b_err_path[:, j_ind, t + 1:t + S + 1]) # print('t=', t, 'j=', j_ind, ', Max Err=', "%10.4e" % # results_bn.fun.max()) # if np.absolute(results_bn.fun).max() > 1e-10: # print(results_bn.success) # print('b=', bvec, ', n=', nvec, ', b_err=', b_errors, # ', n_err=', n_errors, ', c=', cvec) return cpath, npath, bpath, n_err_path, b_err_path
def get_SS(r_init, args, graphs=False): ''' -------------------------------------------------------------------- Solve for the steady-state solution of the S-period-lived agent OG model with endogenous labor supply using the bisection method in K and L for the outer loop -------------------------------------------------------------------- INPUTS: init_vals = length 2 tuple, (rss_init, c1_init) args = length 15 tuple, (S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, Bsct_Tol, Eul_Tol, EulDiff, xi, maxiter) graphs = boolean, =True if output steady-state graphs OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION: firms.get_r() firms.get_w() utils.print_time() inner_loop() creat_graphs() OBJECTS CREATED WITHIN FUNCTION: start_time = scalar > 0, clock time at beginning of program r_init = scalar > -delta, initial guess for steady-state interest rate c1_init = scalar > 0, initial guess for first period consumpt'n S = integer in [3, 80], number of periods an individual lives beta = scalar in (0,1), discount factor for each model per sigma = scalar > 0, coefficient of relative risk aversion l_tilde = scalar > 0, time endowment for each agent each period b_ellip = scalar > 0, fitted value of b for elliptical disutility of labor upsilon = scalar > 1, fitted value of upsilon for elliptical disutility of labor chi_n_vec = (S,) vector, values for chi^n_s A = scalar > 0, total factor productivity parameter in firms' production function alpha = scalar in (0,1), capital share of income delta = scalar in [0,1], model-period depreciation rate of capital Bsct_Tol = scalar > 0, tolderance level for outer-loop bisection method Eul_Tol = scalar > 0, tolerance level for inner-loop root finder EulDiff = Boolean, =True if want difference version of Euler errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1 xi = scalar in (0, 1], SS updating parameter in outer-loop bisection method maxiter = integer >= 1, maximum number of iterations in outer loop bisection method iter_SS = integer >= 0, index of iteration number dist = scalar > 0, distance metric for current iteration rw_params = length 3 tuple, (A, alpha, delta) args to pass into firms.get_r() and firms.get_w() w_init = scalar, initial value for wage inner_args = length 14 tuple, args to pass into inner_loop() K_new = scalar > 0, updated K given r_init, w_init, and bvec L_new = scalar > 0, updated L given r_init, w_init, and nvec cvec = (S,) vector, updated values for lifetime consumption nvec = (S,) vector, updated values for lifetime labor supply bvec = (S,) vector, updated values for lifetime savings (b1, b2,...bS) b_Sp1 = scalar, updated value for savings in last period, should be arbitrarily close to zero r_new = scalar > 0, updated interest rate given bvec and nvec w_new = scalar > 0, updated wage given bvec and nvec n_errors = (S,) vector, labor supply Euler errors given r_init and w_init b_errors = (S-1,) vector, savings Euler errors given r_init and w_init all_errors = (2S,) vector, (n_errors, b_errors, b_Sp1) c_ss = (S,) vector, steady-state lifetime consumption n_ss = (S,) vector, steady-state lifetime labor supply b_ss = (S,) vector, steady-state wealth enter period with (b1, b2, ...bS) b_Sp1_ss = scalar, steady-state savings for period after last period of life. b_Sp1_ss approx. 0 in equilibrium n_err_ss = (S,) vector, lifetime labor supply Euler errors b_err_ss = (S-1) vector, lifetime savings Euler errors r_ss = scalar > 0, steady-state interest rate w_ss = scalar > 0, steady-state wage K_ss = scalar > 0, steady-state aggregate capital stock L_ss = scalar > 0, steady-state aggregate labor Y_params = length 2 tuple, (A, alpha) Y_ss = scalar > 0, steady-state aggregate output (GDP) C_ss = scalar > 0, steady-state aggregate consumption RCerr_ss = scalar, resource constraint error ss_time = scalar, seconds elapsed for steady-state computation ss_output = length 14 dict, steady-state objects {n_ss, b_ss, c_ss, b_Sp1_ss, w_ss, r_ss, K_ss, L_ss, Y_ss, C_ss, n_err_ss, b_err_ss, RCerr_ss, ss_time} FILES CREATED BY THIS FUNCTION: SS_bc.png SS_n.png RETURNS: ss_output -------------------------------------------------------------------- ''' start_time = time.clock() (S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, SS_Tol, Eul_Tol, EulDiff, xi, maxiter) = args ns_guess = 0.4 * l_tilde * np.ones(S) bsp1_guess = 0.1 * np.ones(S - 1) nb_guess = np.append(ns_guess, bsp1_guess) r_args = (nb_guess, S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta, Eul_Tol, EulDiff) results_r = opt.root(get_r_error, r_init, args=r_args, tol=SS_Tol) if results_r.success: print('SS SUCCESS: Steady-state outer loop for r converged.') r_ss = results_r.x r_err_ss = results_r.fun # Solve for steady-state w as a function of steady-state r w_args = (A, alpha, delta) w_ss = firms.get_w(r_ss, w_args) # Solve for steady-state n_s and b_s given steady-state r and w euler_args = (r_ss, w_ss, beta, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, EulDiff, S) results_nb = opt.root(euler_sys, nb_guess, args=euler_args, method='lm', tol=Eul_Tol) n_ss = results_nb.x[:S] bsp1_ss = results_nb.x[S:] n_err_ss = results_nb.fun[:S] b_err_ss = results_nb.fun[S:] b_ss = np.append(0.0, bsp1_ss) c_ss = hh.get_cons(r_ss, w_ss, b_ss, np.append(bsp1_ss, 0.0), n_ss) K_ss = bsp1_ss.sum() L_ss = n_ss.sum() I_ss = delta * K_ss Y_params = (A, alpha) Y_ss = aggr.get_Y(K_ss, L_ss, Y_params) C_ss = aggr.get_C(c_ss) RCerr_ss = Y_ss - C_ss - I_ss ss_time = time.clock() - start_time ss_output = { 'c_ss': c_ss, 'n_ss': n_ss, 'b_ss': b_ss, 'w_ss': w_ss, 'r_ss': r_ss, 'K_ss': K_ss, 'L_ss': L_ss, 'Y_ss': Y_ss, 'I_ss': I_ss, 'C_ss': C_ss, 'n_err_ss': n_err_ss, 'b_err_ss': b_err_ss, 'r_err_ss': r_err_ss, 'RCerr_ss': RCerr_ss, 'ss_time': ss_time } print('n_ss is: ', n_ss) print('b_ss is: ', b_ss) print('K_ss=', K_ss, ', L_ss=', L_ss) print('Y_ss=', Y_ss, ', I_ss=', I_ss, ', C_ss=', C_ss) print('r_ss=', r_ss, ', w_ss=', w_ss) print('Maximum abs. labor supply Euler error is: ', np.absolute(n_err_ss).max()) print('Maximum abs. savings Euler error is: ', np.absolute(b_err_ss).max()) print('Interest rate FOC error is: ', r_err_ss) print('Resource constraint error is: ', RCerr_ss) # Print SS computation time utils.print_time(ss_time, 'SS') if graphs: create_graphs(c_ss, b_ss, n_ss) return ss_output