Ejemplo n.º 1
0
def test_get_y():
    '''
    Test of household.get_y() function.
    '''
    r_hh = np.array([0.05, 0.04, 0.09])
    w = np.array([1.2, 0.8, 2.5])
    b_s = np.array([0.5, 0.99, 9])
    n = np.array([0.8, 3.2, 0.2])
    expected_y = np.array([0.9754, 3.8796, 0.91])
    p = Specifications()
    # p.update_specifications({'S': 4, 'J': 1})
    p.S = 3
    p.e = np.array([0.99, 1.5, 0.2])

    test_y = household.get_y(r_hh, w, b_s, n, p)

    assert np.allclose(test_y, expected_y)
Ejemplo n.º 2
0
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-Core Specifications object): model parameters
        client (Dask client object): client

    Returns:
        output (dictionary): dictionary with steady state solution
            results

    '''
    dist = 10
    iteration = 0
    dist_vec = np.zeros(p.maxiter)
    maxiter_ss = p.maxiter
    nu_ss = p.nu
    if fsolve_flag:  # case where already solved via SS_fsolve
        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_p,
         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)
        if p.baseline_spending:
            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()
        else:
            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()
        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
        if VERBOSE:
            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)
    TR_ss = TR
    Lss = aggr.get_L(nssmat, p, 'SS')
    Bss = aggr.get_B(bssmat_splus1, p, 'SS', False)
    (Dss, D_d_ss, D_f_ss, new_borrowing, debt_service,
     new_borrowing_f) = fiscal.get_D_ss(r_gov_ss, Y, p)
    K_demand_open_ss = firm.get_K(Lss, p.world_int_rate[-1], p, 'SS')
    Kss, K_d_ss, K_f_ss = aggr.get_K_splits(Bss, K_demand_open_ss, D_d_ss,
                                            p.zeta_K[-1])
    Yss = firm.get_Y(Kss, Lss, p, 'SS')
    r_p_ss = aggr.get_r_p(rss, r_gov_ss, Kss, Dss)
    # 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')
    wss = new_w
    BQss = new_BQ
    factor_ss = factor
    bqssmat = household.get_bq(BQss, None, p, 'SS')
    trssmat = household.get_tr(TR_ss, None, p, 'SS')
    ubissmat = p.ubi_nom_array[-1, :, :] / factor_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_p_ss, wss, bssmat_s, nssmat, factor, True, p.e,
                             etr_params_3D, mtry_params_3D, p)
    mtrx_ss = tax.MTR_income(r_p_ss, wss, bssmat_s, nssmat, factor, False, p.e,
                             etr_params_3D, mtrx_params_3D, p)
    etr_ss = tax.ETR_income(r_p_ss, wss, bssmat_s, nssmat, factor, p.e,
                            etr_params_3D, p)

    taxss = tax.net_taxes(r_p_ss, wss, bssmat_s, nssmat, bqssmat, factor_ss,
                          trssmat, ubissmat, theta, None, None, False, 'SS',
                          p.e, etr_params_3D, p)
    cssmat = household.get_cons(r_p_ss, wss, bssmat_s, bssmat_splus1, nssmat,
                                bqssmat, taxss, p.e, p.tau_c[-1, :, :], p)
    yss_before_tax_mat = household.get_y(r_p_ss, wss, bssmat_s, nssmat, p)
    Css = aggr.get_C(cssmat, p, 'SS')

    (total_tax_revenue, iit_payroll_tax_revenue, agg_pension_outlays,
     UBI_outlays, bequest_tax_revenue, wealth_tax_revenue, cons_tax_revenue,
     business_tax_revenue, payroll_tax_revenue,
     iit_revenue) = aggr.revenue(r_p_ss, wss, bssmat_s, nssmat, bqssmat,
                                 cssmat, Yss, Lss, Kss, factor, ubissmat,
                                 theta, etr_params_3D, p, 'SS')
    Gss = fiscal.get_G_ss(Yss, total_tax_revenue, agg_pension_outlays, TR_ss,
                          UBI_outlays, new_borrowing, debt_service, p)

    # Compute total investment (not just domestic)
    Iss_total = aggr.get_I(None, Kss, Kss, p, 'total_ss')

    # solve resource constraint
    # net foreign borrowing
    debt_service_f = fiscal.get_debt_service_f(r_p_ss, D_f_ss)
    RC = aggr.resource_constraint(Yss, Css, Gss, I_d_ss, K_f_ss,
                                  new_borrowing_f, debt_service_f, r_p_ss, p)
    if VERBOSE:
        print('Foreign debt holdings = ', D_f_ss)
        print('Foreign capital holdings = ', K_f_ss)
        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:, :]
    if VERBOSE:
        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,
        'total_taxes_ss': taxss,
        'ubissmat': ubissmat,
        'r_gov_ss': r_gov_ss,
        'r_p_ss': r_p_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_tax_revenue': total_tax_revenue,
        'business_tax_revenue': business_tax_revenue,
        'iit_payroll_tax_revenue': iit_payroll_tax_revenue,
        'iit_revenue': iit_revenue,
        'payroll_tax_revenue': payroll_tax_revenue,
        'agg_pension_outlays': agg_pension_outlays,
        'UBI_outlays_SS': UBI_outlays,
        'bequest_tax_revenue': bequest_tax_revenue,
        'wealth_tax_revenue': wealth_tax_revenue,
        'cons_tax_revenue': cons_tax_revenue,
        'euler_savings': euler_savings,
        'debt_service_f': debt_service_f,
        'new_borrowing_f': new_borrowing_f,
        'debt_service': debt_service,
        '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
Ejemplo n.º 3
0
def run_TPI(p, client=None):
    '''
    Solve for transition path equilibrium of OG-Core.

    Args:
        p (OG-Core Specifications 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) =\
        initial_values
    (TRbaseline, Gbaseline, D0_baseline) = baseline_values

    # Create time path of UBI household benefits and aggregate UBI outlays
    ubi = p.ubi_nom_array / factor
    UBI = aggr.get_L(ubi[:p.T], p, 'TPI')

    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
    guesses_b = utils.get_initial_path(initial_b, ss_vars['bssmat_splus1'], p,
                                       'ratio')
    guesses_n = utils.get_initial_path(initial_n, ss_vars['nssmat'], p,
                                       'ratio')
    b_mat = guesses_b
    n_mat = guesses_n
    ind = np.arange(p.S)

    # Get path for aggregate savings and labor supply
    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
    K_init = B_init * ss_vars['Kss'] / ss_vars['Bss']
    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)
    r[:p.T] = firm.get_r(Y[:p.T], K[:p.T], p, 'TPI')
    r[p.T:] = ss_vars['rss']
    # For case where economy is small open econ
    r[p.zeta_K == 1] = p.world_int_rate[p.zeta_K == 1]
    # Compute other interest rates
    r_gov = fiscal.get_r_gov(r, p)
    r_p = aggr.get_r_p(r, r_gov, K, ss_vars['Dss'])

    # 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']

    # initial guesses at fiscal vars
    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 pct changes below
        else:
            TR_ss2 = ss_vars['TR_ss']
        TR = np.ones(p.T + p.S) * TR_ss2
        total_tax_revenue = TR - ss_vars['agg_pension_outlays']
        G = np.zeros(p.T + p.S)
        D = np.zeros(p.T + p.S)
        D_d = np.zeros(p.T + p.S)
        D_f = np.zeros(p.T + p.S)
    else:
        if p.baseline_spending:
            TR = TRbaseline
            G = Gbaseline
            G[p.T:] = ss_vars['Gss']
        else:
            TR = p.alpha_T * Y
            G = np.ones(p.T + p.S) * ss_vars['Gss']
        D = np.ones(p.T + p.S) * ss_vars['Dss']
        D_d = D * ss_vars['D_d_ss'] / ss_vars['Dss']
        D_f = D * ss_vars['D_f_ss'] / ss_vars['Dss']
    total_tax_revenue = np.ones(p.T + p.S) * ss_vars['total_tax_revenue']

    # Initialize bequests
    BQ0 = aggr.get_BQ(r_p[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)

    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 not p.budget_balance:
            K[:p.T] = firm.get_K_from_Y(Y[:p.T], r[:p.T], p, 'TPI')

        r_p[:p.T] = aggr.get_r_p(r[:p.T], r_gov[:p.T], K[:p.T], D[:p.T])

        outer_loop_vars = (r, w, r_p, 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,
                                    ubi, j, ind, p))
        if client:
            futures = client.compute(lazy_values, num_workers=p.num_workers)
            results = client.gather(futures)
        else:
            results = 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[:p.T, :, :].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.net_taxes(r_p[:p.T], w[:p.T], bmat_s, n_mat[:p.T, :, :],
                                bqmat[:p.T, :, :], factor, trmat[:p.T, :, :],
                                ubi[:p.T, :, :], theta, 0, None, False, 'TPI',
                                p.e, etr_params_4D, p)
        r_p_path = utils.to_timepath_shape(r_p)
        wpath = utils.to_timepath_shape(w)
        c_mat = household.get_cons(r_p_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 = household.get_y(r_p_path[:p.T, :, :],
                                           wpath[:p.T, :, :],
                                           bmat_s[:p.T, :, :],
                                           n_mat[:p.T, :, :], p)

        (total_tax_rev, iit_payroll_tax_revenue, agg_pension_outlays,
         UBI_outlays, bequest_tax_revenue, wealth_tax_revenue,
         cons_tax_revenue, business_tax_revenue, payroll_tax_revenue,
         iit_revenue) = aggr.revenue(r_p[: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, ubi[:p.T, :, :], theta,
                                     etr_params_4D, p, 'TPI')
        total_tax_revenue[:p.T] = total_tax_rev
        dg_fixed_values = (Y, total_tax_revenue, agg_pension_outlays,
                           UBI_outlays, TR, Gbaseline, D0_baseline)
        (Dnew, G[:p.T], D_d[:p.T], D_f[:p.T], new_borrowing,
         debt_service, new_borrowing_f) =\
            fiscal.D_G_path(r_gov, dg_fixed_values, p)
        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.world_int_rate[:p.T], p, 'TPI')
        K[:p.T], K_d[:p.T], K_f[:p.T] = aggr.get_K_splits(
            B[:p.T], K_demand_open, D_d[:p.T], p.zeta_K[:p.T])
        Ynew = firm.get_Y(K[:p.T], L[:p.T], p, 'TPI')
        rnew = r.copy()
        rnew[:p.T] = firm.get_r(Ynew[:p.T], K[:p.T], p, 'TPI')
        # For case where economy is small open econ
        r[p.zeta_K == 1] = p.world_int_rate[p.zeta_K == 1]
        r_gov_new = fiscal.get_r_gov(rnew, p)
        r_p_new = aggr.get_r_p(rnew[:p.T], r_gov_new[:p.T], K[:p.T],
                               Dnew[: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_p_new[:p.T], b_mat_shift, None, p, 'TPI', False)
        bqmat_new = household.get_bq(BQnew, None, p, 'TPI')
        (total_tax_rev, iit_payroll_tax_revenue, agg_pension_outlays,
         UBI_outlays, bequest_tax_revenue, wealth_tax_revenue,
         cons_tax_revenue, business_tax_revenue, payroll_tax_revenue,
         iit_revenue) = aggr.revenue(r_p_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, ubi[:p.T, :, :], theta,
                                     etr_params_4D, p, 'TPI')
        total_tax_revenue[:p.T] = total_tax_rev
        TR_new = fiscal.get_TR(Ynew[:p.T], TR[:p.T], G[:p.T],
                               total_tax_revenue[:p.T],
                               agg_pension_outlays[:p.T], UBI_outlays[:p.T], p,
                               'TPI')

        # 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[:p.T, :, :].reshape(p.T, p.S, 1, p.mtrx_params.shape[2]),
        (1, 1, p.J, 1))
    mtry_params_4D = np.tile(
        p.mtry_params[:p.T, :, :].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_p_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_p_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_p_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
    # foreign debt service costs
    debt_service_f = fiscal.get_debt_service_f(r_p, D_f)
    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_p[:p.T - 1], p)
    # Compute total investment (not just domestic)
    I_total = aggr.get_I(None, K[1:p.T + 1], K[:p.T], p, 'total_tpi')

    # Compute resource constraint error
    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_tax_revenue': total_tax_revenue,
        'business_tax_revenue': business_tax_revenue,
        'iit_payroll_tax_revenue': iit_payroll_tax_revenue,
        'iit_revenue': iit_revenue,
        'payroll_tax_revenue': payroll_tax_revenue,
        'TR': TR,
        'agg_pension_outlays': agg_pension_outlays,
        'bequest_tax_revenue': bequest_tax_revenue,
        'wealth_tax_revenue': wealth_tax_revenue,
        'cons_tax_revenue': cons_tax_revenue,
        'G': G,
        'D': D,
        'D_f': D_f,
        'D_d': D_d,
        'r': r,
        'r_gov': r_gov,
        'r_p': r_p,
        '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,
        'ubi_path': ubi,
        'UBI_path': UBI
    }

    tpi_dir = os.path.join(p.output_base, "TPI")
    utils.mkdirs(tpi_dir)
    tpi_vars = os.path.join(tpi_dir, "TPI_vars.pkl")
    with open(tpi_vars, "wb") as f:
        pickle.dump(output, f)

    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