def test_plot_imm_fixed_save_fig(tmpdir): E = 0 S = base_params.S age_per_EpS = np.arange(21, S + 21) imm_rates_orig = base_params.imm_rates[0, :] imm_rates_adj = base_params.imm_rates[-1, :] parameter_plots.plot_imm_fixed( age_per_EpS, imm_rates_orig, imm_rates_adj, E, S, output_dir=tmpdir) img = mpimg.imread(os.path.join(tmpdir, 'OrigVsAdjImm.png')) assert isinstance(img, np.ndarray)
def test_plot_imm_fixed(): E = 0 S = base_params.S age_per_EpS = np.arange(21, S + 21) imm_rates_orig = base_params.imm_rates[0, :] imm_rates_adj = base_params.imm_rates[-1, :] fig = parameter_plots.plot_imm_fixed( age_per_EpS, imm_rates_orig, imm_rates_adj, E, S) assert fig
def get_pop_objs(E, S, T, min_yr, max_yr, curr_year, GraphDiag=False): ''' This function produces the demographics objects to be used in the OG-USA model package. Args: E (int): number of model periods in which agent is not economically active, >= 1 S (int): number of model periods in which agent is economically active, >= 3 T (int): number of periods to be simulated in TPI, > 2*S min_yr (int): age in years at which agents are born, >= 0 max_yr (int): age in years at which agents die with certainty, >= 4 curr_year (int): current year for which analysis will begin, >= 2016 GraphDiag (bool): =True if want graphical output and printed diagnostics Returns: pop_dict (dict): includes: omega_path_S (Numpy array), time path of the population distribution from the current state to the steady-state, size T+S x S g_n_SS (scalar): steady-state population growth rate omega_SS (Numpy array): normalized steady-state population distribution, length S surv_rates (Numpy array): survival rates that correspond to each model period of life, length S mort_rates (Numpy array): mortality rates that correspond to each model period of life, length S g_n_path (Numpy array): population growth rates over the time path, length T + S ''' assert curr_year >= 2019 # age_per = np.linspace(min_yr, max_yr, E+S) fert_rates = get_fert(E + S, min_yr, max_yr, graph=False) mort_rates, infmort_rate = get_mort(E + S, min_yr, max_yr, graph=False) mort_rates_S = mort_rates[-S:] imm_rates_orig = get_imm_resid(E + S, min_yr, max_yr) OMEGA_orig = np.zeros((E + S, E + S)) OMEGA_orig[0, :] = ((1 - infmort_rate) * fert_rates + np.hstack( (imm_rates_orig[0], np.zeros(E + S - 1)))) OMEGA_orig[1:, :-1] += np.diag(1 - mort_rates[:-1]) OMEGA_orig[1:, 1:] += np.diag(imm_rates_orig[1:]) # Solve for steady-state population growth rate and steady-state # population distribution by age using eigenvalue and eigenvector # decomposition eigvalues, eigvectors = np.linalg.eig(OMEGA_orig) g_n_SS = (eigvalues[np.isreal(eigvalues)].real).max() - 1 eigvec_raw =\ eigvectors[:, (eigvalues[np.isreal(eigvalues)].real).argmax()].real omega_SS_orig = eigvec_raw / eigvec_raw.sum() # Generate time path of the nonstationary population distribution omega_path_lev = np.zeros((E + S, T + S)) pop_data = pd.read_csv( 'https://www2.census.gov/programs-surveys/popest/technical-documentation/file-layouts/2010-2019/nc-est2019-agesex-res.csv' ) pop_data = (pop_data[pop_data['SEX'] == 0][[ 'AGE', 'POPESTIMATE2016', 'POPESTIMATE2017', 'POPESTIMATE2018', 'POPESTIMATE2019' ]]) pop_data.rename(columns={ 'AGE': 'Age', 'POPESTIMATE2016': '2016', 'POPESTIMATE2017': '2017', 'POPESTIMATE2018': '2018', 'POPESTIMATE2019': '2019' }, inplace=True) pop_data_samp = pop_data[(pop_data['Age'] >= min_yr - 1) & (pop_data['Age'] <= max_yr - 1)] pop_2019 = np.array(pop_data_samp['2019'], dtype='f') # Generate the current population distribution given that E+S might # be less than max_yr-min_yr+1 age_per_EpS = np.arange(1, E + S + 1) pop_2019_EpS = pop_rebin(pop_2019, E + S) pop_2019_pct = pop_2019_EpS / pop_2019_EpS.sum() # Age most recent population data to the current year of analysis pop_curr = pop_2019_EpS.copy() data_year = 2019 pop_next = np.dot(OMEGA_orig, pop_curr) g_n_curr = ( (pop_next[-S:].sum() - pop_curr[-S:].sum()) / pop_curr[-S:].sum() ) # g_n in 2019 pop_past = pop_curr # assume 2018-2019 pop # Age the data to the current year for per in range(curr_year - data_year): pop_next = np.dot(OMEGA_orig, pop_curr) g_n_curr = ((pop_next[-S:].sum() - pop_curr[-S:].sum()) / pop_curr[-S:].sum()) pop_past = pop_curr pop_curr = pop_next # Generate time path of the population distribution omega_path_lev[:, 0] = pop_curr.copy() for per in range(1, T + S): pop_next = np.dot(OMEGA_orig, pop_curr) omega_path_lev[:, per] = pop_next.copy() pop_curr = pop_next.copy() # Force the population distribution after 1.5*S periods to be the # steady-state distribution by adjusting immigration rates, holding # constant mortality, fertility, and SS growth rates imm_tol = 1e-14 fixper = int(1.5 * S) omega_SSfx = (omega_path_lev[:, fixper] / omega_path_lev[:, fixper].sum()) imm_objs = (fert_rates, mort_rates, infmort_rate, omega_path_lev[:, fixper], g_n_SS) imm_fulloutput = opt.fsolve(immsolve, imm_rates_orig, args=(imm_objs), full_output=True, xtol=imm_tol) imm_rates_adj = imm_fulloutput[0] imm_diagdict = imm_fulloutput[1] omega_path_S = (omega_path_lev[-S:, :] / np.tile(omega_path_lev[-S:, :].sum(axis=0), (S, 1))) omega_path_S[:, fixper:] = \ np.tile(omega_path_S[:, fixper].reshape((S, 1)), (1, T + S - fixper)) g_n_path = np.zeros(T + S) g_n_path[0] = g_n_curr.copy() g_n_path[1:] = ((omega_path_lev[-S:, 1:].sum(axis=0) - omega_path_lev[-S:, :-1].sum(axis=0)) / omega_path_lev[-S:, :-1].sum(axis=0)) g_n_path[fixper + 1:] = g_n_SS omega_S_preTP = (pop_past.copy()[-S:]) / (pop_past.copy()[-S:].sum()) imm_rates_mat = np.hstack( (np.tile(np.reshape(imm_rates_orig[E:], (S, 1)), (1, fixper)), np.tile(np.reshape(imm_rates_adj[E:], (S, 1)), (1, T + S - fixper)))) if GraphDiag: # Check whether original SS population distribution is close to # the period-T population distribution omegaSSmaxdif = np.absolute(omega_SS_orig - (omega_path_lev[:, T] / omega_path_lev[:, T].sum())).max() if omegaSSmaxdif > 0.0003: print('POP. WARNING: Max. abs. dist. between original SS ' + "pop. dist'n and period-T pop. dist'n is greater than" + ' 0.0003. It is ' + str(omegaSSmaxdif) + '.') else: print('POP. SUCCESS: orig. SS pop. dist is very close to ' + "period-T pop. dist'n. The maximum absolute " + 'difference is ' + str(omegaSSmaxdif) + '.') # Plot the adjusted steady-state population distribution versus # the original population distribution. The difference should be # small omegaSSvTmaxdiff = np.absolute(omega_SS_orig - omega_SSfx).max() if omegaSSvTmaxdiff > 0.0003: print('POP. WARNING: The maximimum absolute difference ' + 'between any two corresponding points in the original' + ' and adjusted steady-state population ' + 'distributions is' + str(omegaSSvTmaxdiff) + ', ' + 'which is greater than 0.0003.') else: print('POP. SUCCESS: The maximum absolute difference ' + 'between any two corresponding points in the original' + ' and adjusted steady-state population ' + 'distributions is ' + str(omegaSSvTmaxdiff)) # Print whether or not the adjusted immigration rates solved the # zero condition immtol_solved = \ np.absolute(imm_diagdict['fvec'].max()) < imm_tol if immtol_solved: print('POP. SUCCESS: Adjusted immigration rates solved ' + 'with maximum absolute error of ' + str(np.absolute(imm_diagdict['fvec'].max())) + ', which is less than the tolerance of ' + str(imm_tol)) else: print('POP. WARNING: Adjusted immigration rates did not ' + 'solve. Maximum absolute error of ' + str(np.absolute(imm_diagdict['fvec'].max())) + ' is greater than the tolerance of ' + str(imm_tol)) # Test whether the steady-state growth rates implied by the # adjusted OMEGA matrix equals the steady-state growth rate of # the original OMEGA matrix OMEGA2 = np.zeros((E + S, E + S)) OMEGA2[0, :] = ((1 - infmort_rate) * fert_rates + np.hstack( (imm_rates_adj[0], np.zeros(E + S - 1)))) OMEGA2[1:, :-1] += np.diag(1 - mort_rates[:-1]) OMEGA2[1:, 1:] += np.diag(imm_rates_adj[1:]) eigvalues2, eigvectors2 = np.linalg.eig(OMEGA2) g_n_SS_adj = (eigvalues[np.isreal(eigvalues2)].real).max() - 1 if np.max(np.absolute(g_n_SS_adj - g_n_SS)) > 10**(-8): print('FAILURE: The steady-state population growth rate' + ' from adjusted OMEGA is different (diff is ' + str(g_n_SS_adj - g_n_SS) + ') than the steady-' + 'state population growth rate from the original' + ' OMEGA.') elif np.max(np.absolute(g_n_SS_adj - g_n_SS)) <= 10**(-8): print('SUCCESS: The steady-state population growth rate' + ' from adjusted OMEGA is close to (diff is ' + str(g_n_SS_adj - g_n_SS) + ') the steady-' + 'state population growth rate from the original' + ' OMEGA.') # Do another test of the adjusted immigration rates. Create the # new OMEGA matrix implied by the new immigration rates. Plug in # the adjusted steady-state population distribution. Hit is with # the new OMEGA transition matrix and it should return the new # steady-state population distribution omega_new = np.dot(OMEGA2, omega_SSfx) omega_errs = np.absolute(omega_new - omega_SSfx) print('The maximum absolute difference between the adjusted ' + 'steady-state population distribution and the ' + 'distribution generated by hitting the adjusted OMEGA ' + 'transition matrix is ' + str(omega_errs.max())) # Plot the original immigration rates versus the adjusted # immigration rates immratesmaxdiff = \ np.absolute(imm_rates_orig - imm_rates_adj).max() print('The maximum absolute distance between any two points ' + 'of the original immigration rates and adjusted ' + 'immigration rates is ' + str(immratesmaxdiff)) # plots pp.plot_omega_fixed(age_per_EpS, omega_SS_orig, omega_SSfx, E, S, output_dir=OUTPUT_DIR) pp.plot_imm_fixed(age_per_EpS, imm_rates_orig, imm_rates_adj, E, S, output_dir=OUTPUT_DIR) pp.plot_population_path(age_per_EpS, pop_2019_pct, omega_path_lev, omega_SSfx, curr_year, E, S, output_dir=OUTPUT_DIR) # return omega_path_S, g_n_SS, omega_SSfx, survival rates, # mort_rates_S, and g_n_path pop_dict = { 'omega': omega_path_S.T, 'g_n_SS': g_n_SS, 'omega_SS': omega_SSfx[-S:] / omega_SSfx[-S:].sum(), 'surv_rate': 1 - mort_rates_S, 'rho': mort_rates_S, 'g_n': g_n_path, 'imm_rates': imm_rates_mat.T, 'omega_S_preTP': omega_S_preTP } return pop_dict