def euler_equation_solver(guesses, *args): ''' Finds the euler errors for certain b and n, one ability type at a time. Args: guesses (Numpy array): initial guesses for b and n, lenth 2S args (tuple): tuple of arguments (r, w, bq, TR, factor, j, p) w (scalar): real wage rate bq (Numpy array): bequest amounts by age, length S tr (scalar): government transfer amount by age, length S factor (scalar): scaling factor converting model units to dollars p (OG-India Specifcations object): model parameters Returns: errros (Numpy array): errors from FOCs, length 2S ''' (r, w, bq, tr, factor, j, p) = args b_guess = np.array(guesses[:p.S]) n_guess = np.array(guesses[p.S:]) b_s = np.array([0] + list(b_guess[:-1])) b_splus1 = b_guess theta = tax.replacement_rate_vals(n_guess, w, factor, j, p) error1 = household.FOC_savings(r, w, b_s, b_splus1, n_guess, bq, factor, tr, theta, p.e[:, j], p.rho, p.tau_c[-1, :, j], p.etr_params[-1, :, :], p.mtry_params[-1, :, :], None, j, p, 'SS') error2 = household.FOC_labor(r, w, b_s, b_splus1, n_guess, bq, factor, tr, theta, p.chi_n, p.e[:, j], p.tau_c[-1, :, j], p.etr_params[-1, :, :], p.mtrx_params[-1, :, :], None, j, p, 'SS') # Put in constraints for consumption and savings. # According to the euler equations, they can be negative. When # Chi_b is large, they will be. This prevents that from happening. # I'm not sure if the constraints are needed for labor. # But we might as well put them in for now. mask1 = n_guess < 0 mask2 = n_guess > p.ltilde mask3 = b_guess <= 0 mask4 = np.isnan(n_guess) mask5 = np.isnan(b_guess) error2[mask1] = 1e14 error2[mask2] = 1e14 error1[mask3] = 1e14 error1[mask5] = 1e14 error2[mask4] = 1e14 taxes = tax.total_taxes(r, w, b_s, n_guess, bq, factor, tr, theta, None, j, False, 'SS', p.e[:, j], p.etr_params[-1, :, :], p) cons = household.get_cons(r, w, b_s, b_splus1, n_guess, bq, taxes, p.e[:, j], p.tau_c[-1, :, j], p) mask6 = cons < 0 error1[mask6] = 1e14 errors = np.hstack((error1, error2)) return errors
def test_total_taxes(r, w, b, n, bq, factor, tr, theta, t, j, shift, method, e, etr_params, p, expected): # Test function that computes total net taxes for the household # method = ss total_taxes = tax.total_taxes(r, w, b, n, bq, factor, tr, theta, t, j, shift, method, e, etr_params, p) assert np.allclose(total_taxes, expected)
def FOC_labor(r, w, b, b_splus1, n, bq, factor, tr, theta, chi_n, e, tau_c, etr_params, mtrx_params, t, j, p, method): r''' Computes errors for the FOC for labor supply in the steady state. This function is usually looped through over J, so it does one lifetime income group at a time. .. math:: w_t e_{j,s}\bigl(1 - \tau^{mtrx}_{s,t}\bigr)(c_{j,s,t})^{-\sigma} = \chi^n_{s}\biggl(\frac{b}{\tilde{l}}\biggr)\biggl(\frac{n_{j,s,t}}{\tilde{l}}\biggr)^{\upsilon-1}\Biggl[1 - \biggl(\frac{n_{j,s,t}}{\tilde{l}}\biggr)^\upsilon\Biggr]^{\frac{1-\upsilon}{\upsilon}} Args: r (array_like): the real interest rate w (array_like): the real wage rate b (Numpy array): household savings b_splus1 (Numpy array): household savings one period ahead n (Numpy array): household labor supply bq (Numpy array): household bequests received factor (scalar): scaling factor converting model units to dollars tr (Numpy array): government tranfers to household theta (Numpy array): social security replacement rate for each lifetime income group chi_n (Numpy array): utility weight on the disutility of labor supply e (Numpy array): effective labor units tau_c (array_like): consumption tax rates etr_params (Numpy array): parameters of the effective tax rate functions mtrx_params (Numpy array): parameters of the marginal tax rate on labor income functions t (int): model period j (int): index of ability type p (OG-India Specifcations object): model parameters method (str): adjusts calculation dimensions based on 'SS' or 'TPI' Returns: FOC_error (Numpy array): error from FOC for labor supply ''' if method == 'SS': tau_payroll = p.tau_payroll[-1] elif method == 'TPI_scalar': # for 1st donut ring onlye tau_payroll = p.tau_payroll[0] else: length = r.shape[0] tau_payroll = p.tau_payroll[t:t + length] if method == 'TPI': if b.ndim == 2: r = r.reshape(r.shape[0], 1) w = w.reshape(w.shape[0], 1) tau_payroll = tau_payroll.reshape(tau_payroll.shape[0], 1) elif b.ndim == 3: r = utils.to_timepath_shape(r, p) w = utils.to_timepath_shape(w, p) tau_payroll = utils.to_timepath_shape(tau_payroll, p) taxes = tax.total_taxes(r, w, b, n, bq, factor, tr, theta, t, j, False, method, e, etr_params, p) cons = get_cons(r, w, b, b_splus1, n, bq, taxes, e, tau_c, p) deriv = (1 - tau_payroll - tax.MTR_income(r, w, b, n, factor, False, e, etr_params, mtrx_params, p)) FOC_error = (marg_ut_cons(cons, p.sigma) * (1 / (1 + tau_c)) * w * deriv * e - marg_ut_labor(n, chi_n, p)) return FOC_error
def FOC_savings(r, w, b, b_splus1, n, bq, factor, tr, theta, e, rho, tau_c, etr_params, mtry_params, t, j, p, method): r''' Computes Euler errors for the FOC for savings in the steady state. This function is usually looped through over J, so it does one lifetime income group at a time. .. math:: c_{j,s,t}^{-\sigma} = e^{-\sigma g_y}\biggl[\chi^b_j\rho_s(b_{j,s+1,t+1})^{-\sigma} + \beta\bigl(1 - \rho_s\bigr)\Bigl(1 + r_{t+1}\bigl[1 - \tau^{mtry}_{s+1,t+1}\bigr]\Bigr)(c_{j,s+1,t+1})^{-\sigma}\biggr] Args: r (array_like): the real interest rate w (array_like): the real wage rate b (Numpy array): household savings b_splus1 (Numpy array): household savings one period ahead b_splus2 (Numpy array): household savings two periods ahead n (Numpy array): household labor supply bq (Numpy array): household bequests received factor (scalar): scaling factor converting model units to dollars tr (Numpy array): government tranfers to household theta (Numpy array): social security replacement rate for each lifetime income group e (Numpy array): effective labor units rho (Numpy array): mortality rates tau_c (array_like): consumption tax rates etr_params (Numpy array): parameters of the effective tax rate functions mtry_params (Numpy array): parameters of the marginal tax rate on capital income functions t (int): model period j (int): index of ability type p (OG-India Specifcations object): model parameters method (str): adjusts calculation dimensions based on 'SS' or 'TPI' Returns: euler (Numpy array): Euler error from FOC for savings ''' if j is not None: chi_b = p.chi_b[j] else: chi_b = p.chi_b if method == 'TPI': r = utils.to_timepath_shape(r, p) w = utils.to_timepath_shape(w, p) taxes = tax.total_taxes(r, w, b, n, bq, factor, tr, theta, t, j, False, method, e, etr_params, p) cons = get_cons(r, w, b, b_splus1, n, bq, taxes, e, tau_c, p) deriv = ((1 + r) - r * (tax.MTR_income(r, w, b, n, factor, True, e, etr_params, mtry_params, p))) savings_ut = (rho * np.exp(-p.sigma * p.g_y) * chi_b * b_splus1**(-p.sigma)) euler_error = np.zeros_like(n) if n.shape[0] > 1: euler_error[:-1] = ( marg_ut_cons(cons[:-1], p.sigma) * (1 / (1 + tau_c[:-1])) - p.beta * (1 - rho[:-1]) * deriv[1:] * marg_ut_cons(cons[1:], p.sigma) * (1 / (1 + tau_c[1:])) * np.exp(-p.sigma * p.g_y) - savings_ut[:-1]) euler_error[-1] = (marg_ut_cons(cons[-1], p.sigma) * (1 / (1 + tau_c[-1])) - savings_ut[-1]) else: euler_error[-1] = (marg_ut_cons(cons[-1], p.sigma) * (1 / (1 + tau_c[-1])) - savings_ut[-1]) return euler_error
def run_TPI(p, client=None): ''' Solve for transition path equilibrium of OG-India. Args: p (OG-India Specifcations object): model parameters client (Dask client object): client Returns: output (dictionary): dictionary with transition path solution results ''' # unpack tuples of parameters initial_values, ss_vars, theta, baseline_values = get_initial_SS_values(p) (B0, b_sinit, b_splus1init, factor, initial_b, initial_n, D0) = initial_values (TRbaseline, Gbaseline) = baseline_values print('Government spending breakpoints are tG1: ', p.tG1, '; and tG2:', p.tG2) # Initialize guesses at time paths # Make array of initial guesses for labor supply and savings domain = np.linspace(0, p.T, p.T) domain2 = np.tile(domain.reshape(p.T, 1, 1), (1, p.S, p.J)) ending_b = ss_vars['bssmat_splus1'] guesses_b = (-1 / (domain2 + 1)) * (ending_b - initial_b) + ending_b ending_b_tail = np.tile(ending_b.reshape(1, p.S, p.J), (p.S, 1, 1)) guesses_b = np.append(guesses_b, ending_b_tail, axis=0) domain3 = np.tile(np.linspace(0, 1, p.T).reshape(p.T, 1, 1), (1, p.S, p.J)) guesses_n = domain3 * (ss_vars['nssmat'] - initial_n) + initial_n ending_n_tail = np.tile(ss_vars['nssmat'].reshape(1, p.S, p.J), (p.S, 1, 1)) guesses_n = np.append(guesses_n, ending_n_tail, axis=0) b_mat = guesses_b n_mat = guesses_n ind = np.arange(p.S) L_init = np.ones((p.T + p.S, )) * ss_vars['Lss'] B_init = np.ones((p.T + p.S, )) * ss_vars['Bss'] L_init[:p.T] = aggr.get_L(n_mat[:p.T], p, 'TPI') B_init[1:p.T] = aggr.get_B(b_mat[:p.T], p, 'TPI', False)[:p.T - 1] B_init[0] = B0 if not p.small_open: if p.budget_balance: K_init = B_init else: K_init = B_init * ss_vars['Kss'] / ss_vars['Bss'] else: K_init = firm.get_B(L_init, p.firm_r, p, 'TPI') K = K_init K_d = K_init * ss_vars['K_d_ss'] / ss_vars['Kss'] K_f = K_init * ss_vars['K_f_ss'] / ss_vars['Kss'] L = L_init B = B_init Y = np.zeros_like(K) Y[:p.T] = firm.get_Y(K[:p.T], L[:p.T], p, 'TPI') Y[p.T:] = ss_vars['Yss'] r = np.zeros_like(Y) if not p.small_open: r[:p.T] = firm.get_r(Y[:p.T], K[:p.T], p, 'TPI') r[p.T:] = ss_vars['rss'] else: r = p.firm_r # compute w w = np.zeros_like(r) w[:p.T] = firm.get_w_from_r(r[:p.T], p, 'TPI') w[p.T:] = ss_vars['wss'] r_gov = fiscal.get_r_gov(r, p) if p.budget_balance: r_hh = r else: r_hh = aggr.get_r_hh(r, r_gov, K, ss_vars['Dss']) if p.small_open: r_hh = p.hh_r BQ0 = aggr.get_BQ(r[0], initial_b, None, p, 'SS', True) if not p.use_zeta: BQ = np.zeros((p.T + p.S, p.J)) for j in range(p.J): BQ[:, j] = (list(np.linspace(BQ0[j], ss_vars['BQss'][j], p.T)) + [ss_vars['BQss'][j]] * p.S) BQ = np.array(BQ) else: BQ = (list(np.linspace(BQ0, ss_vars['BQss'], p.T)) + [ss_vars['BQss']] * p.S) BQ = np.array(BQ) if p.budget_balance: if np.abs(ss_vars['TR_ss']) < 1e-13: TR_ss2 = 0.0 # sometimes SS is very small but not zero, # even if taxes are zero, this get's rid of the approximation # error, which affects the perc changes below else: TR_ss2 = ss_vars['TR_ss'] TR = np.ones(p.T + p.S) * TR_ss2 total_revenue = TR G = np.zeros(p.T + p.S) elif not p.baseline_spending: TR = p.alpha_T * Y G = np.ones(p.T + p.S) * ss_vars['Gss'] elif p.baseline_spending: TR = TRbaseline TR_new = p.TR # Need to set TR_new for later reference G = Gbaseline G_0 = Gbaseline[0] # Initialize some starting values if p.budget_balance: D = np.zeros(p.T + p.S) else: D = np.ones(p.T + p.S) * ss_vars['Dss'] if ss_vars['Dss'] == 0: D_d = np.zeros(p.T + p.S) D_f = np.zeros(p.T + p.S) else: D_d = D * ss_vars['D_d_ss'] / ss_vars['Dss'] D_f = D * ss_vars['D_f_ss'] / ss_vars['Dss'] total_revenue = np.ones(p.T + p.S) * ss_vars['total_revenue_ss'] TPIiter = 0 TPIdist = 10 euler_errors = np.zeros((p.T, 2 * p.S, p.J)) TPIdist_vec = np.zeros(p.maxiter) # TPI loop while (TPIiter < p.maxiter) and (TPIdist >= p.mindist_TPI): r_gov[:p.T] = fiscal.get_r_gov(r[:p.T], p) if p.budget_balance: r_hh[:p.T] = r[:p.T] else: K[:p.T] = firm.get_K_from_Y(Y[:p.T], r[:p.T], p, 'TPI') r_hh[:p.T] = aggr.get_r_hh(r[:p.T], r_gov[:p.T], K[:p.T], D[:p.T]) if p.small_open: r_hh[:p.T] = p.hh_r[:p.T] outer_loop_vars = (r, w, r_hh, BQ, TR, theta) euler_errors = np.zeros((p.T, 2 * p.S, p.J)) lazy_values = [] for j in range(p.J): guesses = (guesses_b[:, :, j], guesses_n[:, :, j]) lazy_values.append( delayed(inner_loop)(guesses, outer_loop_vars, initial_values, j, ind, p)) results = compute(*lazy_values, scheduler=dask.multiprocessing.get, num_workers=p.num_workers) for j, result in enumerate(results): euler_errors[:, :, j], b_mat[:, :, j], n_mat[:, :, j] = result bmat_s = np.zeros((p.T, p.S, p.J)) bmat_s[0, 1:, :] = initial_b[:-1, :] bmat_s[1:, 1:, :] = b_mat[:p.T - 1, :-1, :] bmat_splus1 = np.zeros((p.T, p.S, p.J)) bmat_splus1[:, :, :] = b_mat[:p.T, :, :] etr_params_4D = np.tile( p.etr_params.reshape(p.T, p.S, 1, p.etr_params.shape[2]), (1, 1, p.J, 1)) bqmat = household.get_bq(BQ, None, p, 'TPI') trmat = household.get_tr(TR, None, p, 'TPI') tax_mat = tax.total_taxes(r_hh[:p.T], w[:p.T], bmat_s, n_mat[:p.T, :, :], bqmat[:p.T, :, :], factor, trmat[:p.T, :, :], theta, 0, None, False, 'TPI', p.e, etr_params_4D, p) r_hh_path = utils.to_timepath_shape(r_hh, p) wpath = utils.to_timepath_shape(w, p) c_mat = household.get_cons(r_hh_path[:p.T, :, :], wpath[:p.T, :, :], bmat_s, bmat_splus1, n_mat[:p.T, :, :], bqmat[:p.T, :, :], tax_mat, p.e, p.tau_c[:p.T, :, :], p) y_before_tax_mat = (r_hh_path[:p.T, :, :] * bmat_s[:p.T, :, :] + wpath[:p.T, :, :] * p.e * n_mat[:p.T, :, :]) if not p.baseline_spending and not p.budget_balance: Y[:p.T] = TR[:p.T] / p.alpha_T[:p.T] # maybe unecessary (total_rev, T_Ipath, T_Ppath, T_BQpath, T_Wpath, T_Cpath, business_revenue) = aggr.revenue( r_hh[:p.T], w[:p.T], bmat_s, n_mat[:p.T, :, :], bqmat[:p.T, :, :], c_mat[:p.T, :, :], Y[:p.T], L[:p.T], K[:p.T], factor, theta, etr_params_4D, p, 'TPI') total_revenue[:p.T] = total_rev # set intial debt value if p.baseline: D0 = p.initial_debt_ratio * Y[0] if not p.baseline_spending: G_0 = p.alpha_G[0] * Y[0] dg_fixed_values = (Y, total_revenue, TR, D0, G_0) Dnew, G[:p.T] = fiscal.D_G_path(r_gov, dg_fixed_values, Gbaseline, p) # Fix initial amount of foreign debt holding D_f[0] = p.initial_foreign_debt_ratio * Dnew[0] for t in range(1, p.T): D_f[t + 1] = (D_f[t] / (np.exp(p.g_y) * (1 + p.g_n[t + 1])) + p.zeta_D[t] * (Dnew[t + 1] - (Dnew[t] / (np.exp(p.g_y) * (1 + p.g_n[t + 1]))))) D_d[:p.T] = Dnew[:p.T] - D_f[:p.T] else: # if budget balance Dnew = np.zeros(p.T + 1) G[:p.T] = np.zeros(p.T) D_f[:p.T] = np.zeros(p.T) D_d[:p.T] = np.zeros(p.T) L[:p.T] = aggr.get_L(n_mat[:p.T], p, 'TPI') B[1:p.T] = aggr.get_B(bmat_splus1[:p.T], p, 'TPI', False)[:p.T - 1] K_demand_open = firm.get_K(L[:p.T], p.firm_r[:p.T], p, 'TPI') K_d[:p.T] = B[:p.T] - D_d[:p.T] if np.any(K_d < 0): print('K_d has negative elements. Setting them ' + 'positive to prevent NAN.') K_d[:p.T] = np.fmax(K_d[:p.T], 0.05 * B[:p.T]) K_f[:p.T] = p.zeta_K[:p.T] * (K_demand_open - B[:p.T] + D_d[:p.T]) K = K_f + K_d if np.any(B) < 0: print('B has negative elements. B[0:9]:', B[0:9]) print('B[T-2:T]:', B[p.T - 2, p.T]) if p.small_open: K[:p.T] = K_demand_open Ynew = firm.get_Y(K[:p.T], L[:p.T], p, 'TPI') rnew = r.copy() if not p.small_open: rnew[:p.T] = firm.get_r(Ynew[:p.T], K[:p.T], p, 'TPI') else: rnew[:p.T] = r[:p.T].copy() r_gov_new = fiscal.get_r_gov(rnew, p) if p.budget_balance: r_hh_new = rnew[:p.T] else: r_hh_new = aggr.get_r_hh(rnew[:p.T], r_gov_new[:p.T], K[:p.T], Dnew[:p.T]) if p.small_open: r_hh_new = p.hh_r[:p.T] # compute w wnew = firm.get_w_from_r(rnew[:p.T], p, 'TPI') b_mat_shift = np.append(np.reshape(initial_b, (1, p.S, p.J)), b_mat[:p.T - 1, :, :], axis=0) BQnew = aggr.get_BQ(r_hh_new[:p.T], b_mat_shift, None, p, 'TPI', False) bqmat_new = household.get_bq(BQnew, None, p, 'TPI') (total_rev, T_Ipath, T_Ppath, T_BQpath, T_Wpath, T_Cpath, business_revenue) = aggr.revenue( r_hh_new[:p.T], wnew[:p.T], bmat_s, n_mat[:p.T, :, :], bqmat_new[:p.T, :, :], c_mat[:p.T, :, :], Ynew[:p.T], L[:p.T], K[:p.T], factor, theta, etr_params_4D, p, 'TPI') total_revenue[:p.T] = total_rev if p.budget_balance: TR_new = total_revenue elif not p.baseline_spending: TR_new = p.alpha_T[:p.T] * Ynew[:p.T] # If baseline_spending==True, no need to update TR, it's fixed # update vars for next iteration w[:p.T] = wnew[:p.T] r[:p.T] = utils.convex_combo(rnew[:p.T], r[:p.T], p.nu) BQ[:p.T] = utils.convex_combo(BQnew[:p.T], BQ[:p.T], p.nu) D[:p.T] = Dnew[:p.T] Y[:p.T] = utils.convex_combo(Ynew[:p.T], Y[:p.T], p.nu) if not p.baseline_spending: TR[:p.T] = utils.convex_combo(TR_new[:p.T], TR[:p.T], p.nu) guesses_b = utils.convex_combo(b_mat, guesses_b, p.nu) guesses_n = utils.convex_combo(n_mat, guesses_n, p.nu) print('r diff: ', (rnew[:p.T] - r[:p.T]).max(), (rnew[:p.T] - r[:p.T]).min()) print('BQ diff: ', (BQnew[:p.T] - BQ[:p.T]).max(), (BQnew[:p.T] - BQ[:p.T]).min()) print('TR diff: ', (TR_new[:p.T] - TR[:p.T]).max(), (TR_new[:p.T] - TR[:p.T]).min()) print('Y diff: ', (Ynew[:p.T] - Y[:p.T]).max(), (Ynew[:p.T] - Y[:p.T]).min()) if not p.baseline_spending: if TR.all() != 0: TPIdist = np.array( list(utils.pct_diff_func(rnew[:p.T], r[:p.T])) + list( utils.pct_diff_func(BQnew[:p.T], BQ[:p.T]).flatten()) + list(utils.pct_diff_func(TR_new[:p.T], TR[:p.T]))).max() else: TPIdist = np.array( list(utils.pct_diff_func(rnew[:p.T], r[:p.T])) + list( utils.pct_diff_func(BQnew[:p.T], BQ[:p.T]).flatten()) + list(np.abs(TR[:p.T]))).max() else: TPIdist = np.array( list(utils.pct_diff_func(rnew[:p.T], r[:p.T])) + list(utils.pct_diff_func(BQnew[:p.T], BQ[:p.T]).flatten()) + list(utils.pct_diff_func(Ynew[:p.T], Y[:p.T]))).max() TPIdist_vec[TPIiter] = TPIdist # After T=10, if cycling occurs, drop the value of nu # wait til after T=10 or so, because sometimes there is a jump up # in the first couple iterations # if TPIiter > 10: # if TPIdist_vec[TPIiter] - TPIdist_vec[TPIiter - 1] > 0: # nu /= 2 # print 'New Value of nu:', nu TPIiter += 1 print('Iteration:', TPIiter) print('\tDistance:', TPIdist) # Compute effective and marginal tax rates for all agents mtrx_params_4D = np.tile( p.mtrx_params.reshape(p.T, p.S, 1, p.mtrx_params.shape[2]), (1, 1, p.J, 1)) mtry_params_4D = np.tile( p.mtry_params.reshape(p.T, p.S, 1, p.mtry_params.shape[2]), (1, 1, p.J, 1)) e_3D = np.tile(p.e.reshape(1, p.S, p.J), (p.T, 1, 1)) mtry_path = tax.MTR_income(r_hh_path[:p.T], wpath[:p.T], bmat_s[:p.T, :, :], n_mat[:p.T, :, :], factor, True, e_3D, etr_params_4D, mtry_params_4D, p) mtrx_path = tax.MTR_income(r_hh_path[:p.T], wpath[:p.T], bmat_s[:p.T, :, :], n_mat[:p.T, :, :], factor, False, e_3D, etr_params_4D, mtrx_params_4D, p) etr_path = tax.ETR_income(r_hh_path[:p.T], wpath[:p.T], bmat_s[:p.T, :, :], n_mat[:p.T, :, :], factor, e_3D, etr_params_4D, p) C = aggr.get_C(c_mat, p, 'TPI') # Note that implicity in this computation is that immigrants' # wealth is all in the form of private capital I_d = aggr.get_I(bmat_splus1[:p.T], K_d[1:p.T + 1], K_d[:p.T], p, 'TPI') I = aggr.get_I(bmat_splus1[:p.T], K[1:p.T + 1], K[:p.T], p, 'TPI') # solve resource constraint # net foreign borrowing new_borrowing_f = (D_f[1:p.T + 1] * np.exp(p.g_y) * (1 + p.g_n[1:p.T + 1]) - D_f[:p.T]) debt_service_f = D_f * r_hh RC_error = aggr.resource_constraint(Y[:p.T - 1], C[:p.T - 1], G[:p.T - 1], I_d[:p.T - 1], K_f[:p.T - 1], new_borrowing_f[:p.T - 1], debt_service_f[:p.T - 1], r_hh[:p.T - 1], p) # Compute total investment (not just domestic) I_total = ((1 + p.g_n[:p.T]) * np.exp(p.g_y) * K[1:p.T + 1] - (1.0 - p.delta) * K[:p.T]) rce_max = np.amax(np.abs(RC_error)) print('Max absolute value resource constraint error:', rce_max) print('Checking time path for violations of constraints.') for t in range(p.T): household.constraint_checker_TPI(b_mat[t], n_mat[t], c_mat[t], t, p.ltilde) eul_savings = euler_errors[:, :p.S, :].max(1).max(1) eul_laborleisure = euler_errors[:, p.S:, :].max(1).max(1) print('Max Euler error, savings: ', eul_savings) print('Max Euler error labor supply: ', eul_laborleisure) ''' ------------------------------------------------------------------------ Save variables/values so they can be used in other modules ------------------------------------------------------------------------ ''' output = { 'Y': Y[:p.T], 'B': B, 'K': K, 'K_f': K_f, 'K_d': K_d, 'L': L, 'C': C, 'I': I, 'I_total': I_total, 'I_d': I_d, 'BQ': BQ, 'total_revenue': total_revenue, 'business_revenue': business_revenue, 'IITpayroll_revenue': T_Ipath, 'TR': TR, 'T_P': T_Ppath, 'T_BQ': T_BQpath, 'T_W': T_Wpath, 'T_C': T_Cpath, 'G': G, 'D': D, 'D_f': D_f, 'D_d': D_d, 'r': r, 'r_gov': r_gov, 'r_hh': r_hh, 'w': w, 'bmat_splus1': bmat_splus1, 'bmat_s': bmat_s[:p.T, :, :], 'n_mat': n_mat[:p.T, :, :], 'c_path': c_mat, 'bq_path': bqmat, 'tr_path': trmat, 'y_before_tax_mat': y_before_tax_mat, 'tax_path': tax_mat, 'eul_savings': eul_savings, 'eul_laborleisure': eul_laborleisure, 'resource_constraint_error': RC_error, 'new_borrowing_f': new_borrowing_f, 'debt_service_f': debt_service_f, 'etr_path': etr_path, 'mtrx_path': mtrx_path, 'mtry_path': mtry_path } tpi_dir = os.path.join(p.output_base, "TPI") utils.mkdirs(tpi_dir) tpi_vars = os.path.join(tpi_dir, "TPI_vars.pkl") pickle.dump(output, open(tpi_vars, "wb")) if np.any(G) < 0: print('Government spending is negative along transition path' + ' to satisfy budget') if (((TPIiter >= p.maxiter) or (np.absolute(TPIdist) > p.mindist_TPI)) and ENFORCE_SOLUTION_CHECKS): raise RuntimeError('Transition path equlibrium not found' + ' (TPIdist)') if ((np.any(np.absolute(RC_error) >= p.mindist_TPI * 10)) and ENFORCE_SOLUTION_CHECKS): raise RuntimeError('Transition path equlibrium not found ' + '(RC_error)') if ((np.any(np.absolute(eul_savings) >= p.mindist_TPI) or (np.any(np.absolute(eul_laborleisure) > p.mindist_TPI))) and ENFORCE_SOLUTION_CHECKS): raise RuntimeError('Transition path equlibrium not found ' + '(eulers)') return output
def inner_loop(outer_loop_vars, p, client): ''' This function solves for the inner loop of the SS. That is, given the guesses of the outer loop variables (r, w, TR, factor) this function solves the households' problems in the SS. Args: outer_loop_vars (tuple): tuple of outer loop variables, (bssmat, nssmat, r, BQ, TR, factor) or (bssmat, nssmat, r, BQ, Y, TR, factor) bssmat (Numpy array): initial guess at savings, size = SxJ nssmat (Numpy array): initial guess at labor supply, size = SxJ BQ (array_like): aggregate bequest amount(s) Y (scalar): real GDP TR (scalar): lump sum transfer amount factor (scalar): scaling factor converting model units to dollars w (scalar): real wage rate p (OG-India Specifcations object): model parameters client (Dask client object): client Returns: euler_errors (Numpy array): errors terms from FOCs, size = 2SxJ bssmat (Numpy array): savings, size = SxJ nssmat (Numpy array): labor supply, size = SxJ new_r (scalar): real interest rate on firm capital new_r_gov (scalar): real interest rate on government debt new_r_hh (scalar): real interest rate on household portfolio new_w (scalar): real wage rate new_TR (scalar): lump sum transfer amount new_Y (scalar): real GDP new_factor (scalar): scaling factor converting model units to dollars new_BQ (array_like): aggregate bequest amount(s) average_income_model (scalar): average income in model units ''' # unpack variables to pass to function if p.budget_balance: bssmat, nssmat, r, BQ, TR, factor = outer_loop_vars else: bssmat, nssmat, r, BQ, Y, TR, factor = outer_loop_vars euler_errors = np.zeros((2 * p.S, p.J)) w = firm.get_w_from_r(r, p, 'SS') r_gov = fiscal.get_r_gov(r, p) if p.budget_balance: r_hh = r D = 0 else: D = p.debt_ratio_ss * Y K = firm.get_K_from_Y(Y, r, p, 'SS') r_hh = aggr.get_r_hh(r, r_gov, K, D) if p.small_open: r_hh = p.hh_r[-1] bq = household.get_bq(BQ, None, p, 'SS') tr = household.get_tr(TR, None, p, 'SS') lazy_values = [] for j in range(p.J): guesses = np.append(bssmat[:, j], nssmat[:, j]) euler_params = (r_hh, w, bq[:, j], tr[:, j], factor, j, p) lazy_values.append( delayed(opt.fsolve)(euler_equation_solver, guesses * .9, args=euler_params, xtol=MINIMIZER_TOL, full_output=True)) results = compute(*lazy_values, scheduler=dask.multiprocessing.get, num_workers=p.num_workers) # for j, result in results.items(): for j, result in enumerate(results): [solutions, infodict, ier, message] = result euler_errors[:, j] = infodict['fvec'] bssmat[:, j] = solutions[:p.S] nssmat[:, j] = solutions[p.S:] L = aggr.get_L(nssmat, p, 'SS') B = aggr.get_B(bssmat, p, 'SS', False) K_demand_open = firm.get_K(L, p.firm_r[-1], p, 'SS') D_f = p.zeta_D[-1] * D D_d = D - D_f if not p.small_open: K_d = B - D_d K_f = p.zeta_K[-1] * (K_demand_open - B + D_d) K = K_f + K_d else: # can remove this else statement by making small open the case # where zeta_K = 1 K_d = B - D_d K_f = K_demand_open - B + D_d K = K_f + K_d new_Y = firm.get_Y(K, L, p, 'SS') if p.budget_balance: Y = new_Y if not p.small_open: new_r = firm.get_r(Y, K, p, 'SS') else: new_r = p.firm_r[-1] new_w = firm.get_w_from_r(new_r, p, 'SS') b_s = np.array(list(np.zeros(p.J).reshape(1, p.J)) + list(bssmat[:-1, :])) new_r_gov = fiscal.get_r_gov(new_r, p) new_r_hh = aggr.get_r_hh(new_r, new_r_gov, K, D) average_income_model = ((new_r_hh * b_s + new_w * p.e * nssmat) * p.omega_SS.reshape(p.S, 1) * p.lambdas.reshape(1, p.J)).sum() if p.baseline: new_factor = p.mean_income_data / average_income_model else: new_factor = factor new_BQ = aggr.get_BQ(new_r_hh, bssmat, None, p, 'SS', False) new_bq = household.get_bq(new_BQ, None, p, 'SS') tr = household.get_tr(TR, None, p, 'SS') theta = tax.replacement_rate_vals(nssmat, new_w, new_factor, None, p) if p.budget_balance: etr_params_3D = np.tile( np.reshape(p.etr_params[-1, :, :], (p.S, 1, p.etr_params.shape[2])), (1, p.J, 1)) taxss = tax.total_taxes(new_r_hh, new_w, b_s, nssmat, new_bq, factor, tr, theta, None, None, False, 'SS', p.e, etr_params_3D, p) cssmat = household.get_cons(new_r_hh, new_w, b_s, bssmat, nssmat, new_bq, taxss, p.e, p.tau_c[-1, :, :], p) new_TR, _, _, _, _, _, _ = aggr.revenue(new_r_hh, new_w, b_s, nssmat, new_bq, cssmat, new_Y, L, K, factor, theta, etr_params_3D, p, 'SS') elif p.baseline_spending: new_TR = TR else: new_TR = p.alpha_T[-1] * new_Y return euler_errors, bssmat, nssmat, new_r, new_r_gov, new_r_hh, \ new_w, new_TR, new_Y, new_factor, new_BQ, average_income_model
def SS_solver(bmat, nmat, r, BQ, TR, factor, Y, p, client, fsolve_flag=False): ''' Solves for the steady state distribution of capital, labor, as well as w, r, TR and the scaling factor, using functional iteration. Args: bmat (Numpy array): initial guess at savings, size = SxJ nmat (Numpy array): initial guess at labor supply, size = SxJ r (scalar): real interest rate BQ (array_like): aggregate bequest amount(s) TR (scalar): lump sum transfer amount factor (scalar): scaling factor converting model units to dollars Y (scalar): real GDP p (OG-India Specifcations object): model parameters client (Dask client object): client Returns: output (dictionary): dictionary with steady state solution results ''' # Rename the inputs if not p.budget_balance: if not p.baseline_spending: Y = TR / p.alpha_T[-1] if p.small_open: r = p.hh_r[-1] dist = 10 iteration = 0 dist_vec = np.zeros(p.maxiter) maxiter_ss = p.maxiter nu_ss = p.nu if fsolve_flag: maxiter_ss = 1 while (dist > p.mindist_SS) and (iteration < maxiter_ss): # Solve for the steady state levels of b and n, given w, r, # Y and factor if p.budget_balance: outer_loop_vars = (bmat, nmat, r, BQ, TR, factor) else: outer_loop_vars = (bmat, nmat, r, BQ, Y, TR, factor) (euler_errors, new_bmat, new_nmat, new_r, new_r_gov, new_r_hh, new_w, new_TR, new_Y, new_factor, new_BQ, average_income_model) =\ inner_loop(outer_loop_vars, p, client) r = utils.convex_combo(new_r, r, nu_ss) factor = utils.convex_combo(new_factor, factor, nu_ss) BQ = utils.convex_combo(new_BQ, BQ, nu_ss) # bmat = utils.convex_combo(new_bmat, bmat, nu_ss) # nmat = utils.convex_combo(new_nmat, nmat, nu_ss) if not p.baseline_spending: TR = utils.convex_combo(new_TR, TR, nu_ss) dist = np.array([utils.pct_diff_func(new_r, r)] + list(utils.pct_diff_func(new_BQ, BQ)) + [utils.pct_diff_func(new_TR, TR)] + [utils.pct_diff_func(new_factor, factor)]).max() else: Y = utils.convex_combo(new_Y, Y, nu_ss) if Y != 0: dist = np.array( [utils.pct_diff_func(new_r, r)] + list(utils.pct_diff_func(new_BQ, BQ)) + [utils.pct_diff_func(new_Y, Y)] + [utils.pct_diff_func(new_factor, factor)]).max() else: # If Y is zero (if there is no output), a percent difference # will throw NaN's, so we use an absolute difference dist = np.array( [utils.pct_diff_func(new_r, r)] + list(utils.pct_diff_func(new_BQ, BQ)) + [abs(new_Y - Y)] + [utils.pct_diff_func(new_factor, factor)]).max() dist_vec[iteration] = dist # Similar to TPI: if the distance between iterations increases, then # decrease the value of nu to prevent cycling if iteration > 10: if dist_vec[iteration] - dist_vec[iteration - 1] > 0: nu_ss /= 2.0 print('New value of nu:', nu_ss) iteration += 1 print('Iteration: %02d' % iteration, ' Distance: ', dist) # Generate the SS values of variables, including euler errors bssmat_s = np.append(np.zeros((1, p.J)), bmat[:-1, :], axis=0) bssmat_splus1 = bmat nssmat = nmat rss = r r_gov_ss = fiscal.get_r_gov(rss, p) if p.budget_balance: r_hh_ss = rss Dss = 0.0 else: Dss = p.debt_ratio_ss * Y Lss = aggr.get_L(nssmat, p, 'SS') Bss = aggr.get_B(bssmat_splus1, p, 'SS', False) K_demand_open_ss = firm.get_K(Lss, p.firm_r[-1], p, 'SS') D_f_ss = p.zeta_D[-1] * Dss D_d_ss = Dss - D_f_ss K_d_ss = Bss - D_d_ss if not p.small_open: K_f_ss = p.zeta_K[-1] * (K_demand_open_ss - Bss + D_d_ss) Kss = K_f_ss + K_d_ss # Note that implicity in this computation is that immigrants' # wealth is all in the form of private capital I_d_ss = aggr.get_I(bssmat_splus1, K_d_ss, K_d_ss, p, 'SS') Iss = aggr.get_I(bssmat_splus1, Kss, Kss, p, 'SS') else: K_d_ss = Bss - D_d_ss K_f_ss = K_demand_open_ss - Bss + D_d_ss Kss = K_f_ss + K_d_ss InvestmentPlaceholder = np.zeros(bssmat_splus1.shape) Iss = aggr.get_I(InvestmentPlaceholder, Kss, Kss, p, 'SS') I_d_ss = aggr.get_I(bssmat_splus1, K_d_ss, K_d_ss, p, 'SS') r_hh_ss = aggr.get_r_hh(rss, r_gov_ss, Kss, Dss) wss = new_w BQss = new_BQ factor_ss = factor TR_ss = TR bqssmat = household.get_bq(BQss, None, p, 'SS') trssmat = household.get_tr(TR_ss, None, p, 'SS') Yss = firm.get_Y(Kss, Lss, p, 'SS') theta = tax.replacement_rate_vals(nssmat, wss, factor_ss, None, p) # Compute effective and marginal tax rates for all agents etr_params_3D = np.tile( np.reshape(p.etr_params[-1, :, :], (p.S, 1, p.etr_params.shape[2])), (1, p.J, 1)) mtrx_params_3D = np.tile( np.reshape(p.mtrx_params[-1, :, :], (p.S, 1, p.mtrx_params.shape[2])), (1, p.J, 1)) mtry_params_3D = np.tile( np.reshape(p.mtry_params[-1, :, :], (p.S, 1, p.mtry_params.shape[2])), (1, p.J, 1)) mtry_ss = tax.MTR_income(r_hh_ss, wss, bssmat_s, nssmat, factor, True, p.e, etr_params_3D, mtry_params_3D, p) mtrx_ss = tax.MTR_income(r_hh_ss, wss, bssmat_s, nssmat, factor, False, p.e, etr_params_3D, mtrx_params_3D, p) etr_ss = tax.ETR_income(r_hh_ss, wss, bssmat_s, nssmat, factor, p.e, etr_params_3D, p) taxss = tax.total_taxes(r_hh_ss, wss, bssmat_s, nssmat, bqssmat, factor_ss, trssmat, theta, None, None, False, 'SS', p.e, etr_params_3D, p) cssmat = household.get_cons(r_hh_ss, wss, bssmat_s, bssmat_splus1, nssmat, bqssmat, taxss, p.e, p.tau_c[-1, :, :], p) yss_before_tax_mat = r_hh_ss * bssmat_s + wss * p.e * nssmat Css = aggr.get_C(cssmat, p, 'SS') (total_revenue_ss, T_Iss, T_Pss, T_BQss, T_Wss, T_Css, business_revenue) =\ aggr.revenue(r_hh_ss, wss, bssmat_s, nssmat, bqssmat, cssmat, Yss, Lss, Kss, factor, theta, etr_params_3D, p, 'SS') debt_service_ss = r_gov_ss * Dss new_borrowing = Dss * ((1 + p.g_n_ss) * np.exp(p.g_y) - 1) # government spends such that it expands its debt at the same rate as GDP if p.budget_balance: Gss = 0.0 else: Gss = total_revenue_ss + new_borrowing - (TR_ss + debt_service_ss) print('G components = ', new_borrowing, TR_ss, debt_service_ss) # Compute total investment (not just domestic) Iss_total = ((1 + p.g_n_ss) * np.exp(p.g_y) - 1 + p.delta) * Kss # solve resource constraint # net foreign borrowing print('Foreign debt holdings = ', D_f_ss) print('Foreign capital holdings = ', K_f_ss) new_borrowing_f = D_f_ss * (np.exp(p.g_y) * (1 + p.g_n_ss) - 1) debt_service_f = D_f_ss * r_hh_ss RC = aggr.resource_constraint(Yss, Css, Gss, I_d_ss, K_f_ss, new_borrowing_f, debt_service_f, r_hh_ss, p) print('resource constraint: ', RC) if Gss < 0: print('Steady state government spending is negative to satisfy' + ' budget') if ENFORCE_SOLUTION_CHECKS and (np.absolute(RC) > p.mindist_SS): print('Resource Constraint Difference:', RC) err = 'Steady state aggregate resource constraint not satisfied' raise RuntimeError(err) # check constraints household.constraint_checker_SS(bssmat_splus1, nssmat, cssmat, p.ltilde) euler_savings = euler_errors[:p.S, :] euler_labor_leisure = euler_errors[p.S:, :] print('Maximum error in labor FOC = ', np.absolute(euler_labor_leisure).max()) print('Maximum error in savings FOC = ', np.absolute(euler_savings).max()) # Return dictionary of SS results output = { 'Kss': Kss, 'K_f_ss': K_f_ss, 'K_d_ss': K_d_ss, 'Bss': Bss, 'Lss': Lss, 'Css': Css, 'Iss': Iss, 'Iss_total': Iss_total, 'I_d_ss': I_d_ss, 'nssmat': nssmat, 'Yss': Yss, 'Dss': Dss, 'D_f_ss': D_f_ss, 'D_d_ss': D_d_ss, 'wss': wss, 'rss': rss, 'r_gov_ss': r_gov_ss, 'r_hh_ss': r_hh_ss, 'theta': theta, 'BQss': BQss, 'factor_ss': factor_ss, 'bssmat_s': bssmat_s, 'cssmat': cssmat, 'bssmat_splus1': bssmat_splus1, 'yss_before_tax_mat': yss_before_tax_mat, 'bqssmat': bqssmat, 'TR_ss': TR_ss, 'trssmat': trssmat, 'Gss': Gss, 'total_revenue_ss': total_revenue_ss, 'business_revenue': business_revenue, 'IITpayroll_revenue': T_Iss, 'T_Pss': T_Pss, 'T_BQss': T_BQss, 'T_Wss': T_Wss, 'T_Css': T_Css, 'euler_savings': euler_savings, 'debt_service_f': debt_service_f, 'new_borrowing_f': new_borrowing_f, 'debt_service_ss': debt_service_ss, 'new_borrowing': new_borrowing, 'euler_labor_leisure': euler_labor_leisure, 'resource_constraint_error': RC, 'etr_ss': etr_ss, 'mtrx_ss': mtrx_ss, 'mtry_ss': mtry_ss } return output