def test_get_cons(model_args, expected): # Test consumption calculation r, w, b, b_splus1, n, bq, net_tax, tau_c, p = model_args test_value = household.get_cons(r, w, b, b_splus1, n, bq, net_tax, p.e, tau_c, p) assert np.allclose(test_value, expected)
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 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