Example #1
0
def euler_savings_func(w, r, e, n_guess, b_s, b_splus1, b_splus2, BQ, factor,
                       T_H, chi_b, params, theta, tau_bq, rho, lambdas):
    '''
    This function is usually looped through over J, so it does one ability group at a time.
    Inputs:
        w = wage rate (scalar)
        r = rental rate (scalar)
        e = ability levels (Sx1 array)
        n_guess = labor distribution (Sx1 array)
        b_s = wealth holdings at the start of a period (Sx1 array)
        b_splus1 = wealth holdings for the next period (Sx1 array)
        b_splus2 = wealth holdings for 2 periods ahead (Sx1 array)
        BQ = aggregate bequests for a certain ability (scalar)
        factor = scaling factor to convert to dollars (scalar)
        T_H = lump sum tax (scalar)
        chi_b = chi^b_j for a certain ability (scalar)
        params = parameter list (list)
        theta = replacement rate for a certain ability (scalar)
        tau_bq = bequest tax rate (scalar)
        rho = mortality rate (Sx1 array)
        lambdas = ability weight (scalar)
    Output:
        euler = Value of savings euler error (Sx1 array)
    '''
    J, S, T, beta, sigma, alpha, Z, delta, ltilde, nu, g_y, g_n_ss, tau_payroll, retire, mean_income_data, \
        a_tax_income, b_tax_income, c_tax_income, d_tax_income, h_wealth, p_wealth, m_wealth, b_ellipse, upsilon = params
    # In order to not have 2 savings euler equations (one that solves the first S-1 equations, and one that solves the last one),
    # we combine them.  In order to do this, we have to compute a consumption term in period t+1, which requires us to have a shifted
    # e and n matrix.  We append a zero on the end of both of these so they will be the right size.  We could append any value to them,
    # since in the euler equation, the coefficient on the marginal utility of
    # consumption for this term will be zero (since rho is one).
    e_extended = np.array(list(e) + [0])
    n_extended = np.array(list(n_guess) + [0])
    tax1 = tax.total_taxes(r, b_s, w, e, n_guess, BQ, lambdas, factor, T_H,
                           None, 'SS', False, params, theta, tau_bq)
    tax2 = tax.total_taxes(r, b_splus1, w, e_extended[1:], n_extended[1:], BQ,
                           lambdas, factor, T_H, None, 'SS', True, params,
                           theta, tau_bq)
    cons1 = get_cons(r, b_s, w, e, n_guess, BQ, lambdas, b_splus1, params,
                     tax1)
    cons2 = get_cons(r, b_splus1, w, e_extended[1:], n_extended[1:], BQ,
                     lambdas, b_splus2, params, tax2)
    income = (r * b_splus1 + w * e_extended[1:] * n_extended[1:]) * factor
    deriv = (1 + r *
             (1 - tax.tau_income(r, b_splus1, w, e_extended[1:],
                                 n_extended[1:], factor, params) -
              tax.tau_income_deriv(r, b_splus1, w, e_extended[1:],
                                   n_extended[1:], factor, params) * income) -
             tax.tau_w_prime(b_splus1, params) * b_splus1 -
             tax.tau_wealth(b_splus1, params))
    savings_ut = rho * np.exp(-sigma * g_y) * chi_b * b_splus1**(-sigma)
    # Again, not who in this equation, the (1-rho) term will zero out in the last period, so the last entry of cons2 can be complete
    # gibberish (which it is).  It just has to exist so cons2 is the right
    # size to match all other arrays in the equation.
    euler = marg_ut_cons(cons1,
                         params) - beta * (1 - rho) * deriv * marg_ut_cons(
                             cons2, params) * np.exp(-sigma * g_y) - savings_ut
    return euler
Example #2
0
def Steady_state_TPI_solver(guesses, winit, rinit, BQinit, T_H_init, factor, j,
                            s, t, params, theta, tau_bq, rho, lambdas, e,
                            initial_b, chi_b, chi_n):
    '''
    Parameters:
        guesses = distribution of capital and labor (various length list)
        winit   = wage rate ((T+S)x1 array)
        rinit   = rental rate ((T+S)x1 array)
        BQinit = aggregate bequests ((T+S)x1 array)
        T_H_init = lump sum tax over time ((T+S)x1 array)
        factor = scaling factor (scalar)
        j = which ability type is being solved for (scalar)
        s = which upper triangle loop is being solved for (scalar)
        t = which diagonal is being solved for (scalar)
        params = list of parameters (list)
        theta = replacement rates (Jx1 array)
        tau_bq = bequest tax rate (Jx1 array)
        rho = mortalit rate (Sx1 array)
        lambdas = ability weights (Jx1 array)
        e = ability type (SxJ array)
        initial_b = capital stock distribution in period 0 (SxJ array)
        chi_b = chi^b_j (Jx1 array)
        chi_n = chi^n_s (Sx1 array)
    Output:
        Value of Euler error (various length list)
    '''

    J, S, T, beta, sigma, alpha, Z, delta, ltilde, nu, g_y, g_n_ss, tau_payroll, retire, mean_income_data, \
        a_tax_income, b_tax_income, c_tax_income, d_tax_income, h_wealth, p_wealth, m_wealth, b_ellipse, upsilon = params
    length = len(guesses) / 2
    b_guess = np.array(guesses[:length])
    n_guess = np.array(guesses[length:])

    if length == S:
        b_s = np.array([0] + list(b_guess[:-1]))
    else:
        b_s = np.array([(initial_b[-(s + 3), j])] + list(b_guess[:-1]))
    b_splus1 = b_guess
    b_splus2 = np.array(list(b_guess[1:]) + [0])
    w_s = winit[t:t + length]
    w_splus1 = winit[t + 1:t + length + 1]
    r_s = rinit[t:t + length]
    r_splus1 = rinit[t + 1:t + length + 1]
    n_s = n_guess
    n_extended = np.array(list(n_guess[1:]) + [0])
    e_s = e[-length:, j]
    e_extended = np.array(list(e[-length + 1:, j]) + [0])
    BQ_s = BQinit[t:t + length]
    BQ_splus1 = BQinit[t + 1:t + length + 1]
    T_H_s = T_H_init[t:t + length]
    T_H_splus1 = T_H_init[t + 1:t + length + 1]
    # Savings euler equations
    tax_s = tax.total_taxes(r_s, b_s, w_s, e_s, n_s, BQ_s, lambdas[j], factor,
                            T_H_s, j, 'TPI', False, params, theta, tau_bq)
    tax_splus1 = tax.total_taxes(r_splus1, b_splus1, w_splus1, e_extended,
                                 n_extended, BQ_splus1, lambdas[j], factor,
                                 T_H_splus1, j, 'TPI', True, params, theta,
                                 tau_bq)
    cons_s = household.get_cons(r_s, b_s, w_s, e_s, n_s, BQ_s, lambdas[j],
                                b_splus1, params, tax_s)
    cons_splus1 = household.get_cons(r_splus1, b_splus1, w_splus1, e_extended,
                                     n_extended, BQ_splus1, lambdas[j],
                                     b_splus2, params, tax_splus1)
    income_splus1 = (r_splus1 * b_splus1 +
                     w_splus1 * e_extended * n_extended) * factor
    savings_ut = rho[-(length):] * np.exp(-sigma * g_y) * \
        chi_b[-(length):, j] * b_splus1 ** (-sigma)
    deriv_savings = 1 + r_splus1 * (
        1 - tax.tau_income(r_splus1, b_splus1, w_splus1, e_extended,
                           n_extended, factor, params) -
        tax.tau_income_deriv(r_splus1, b_splus1, w_splus1, e_extended,
                             n_extended, factor, params) * income_splus1
    ) - tax.tau_w_prime(b_splus1, params) * b_splus1 - tax.tau_wealth(
        b_splus1, params)
    error1 = household.marg_ut_cons(
        cons_s, params) - beta * (1 - rho[-(length):]) * np.exp(
            -sigma * g_y) * deriv_savings * household.marg_ut_cons(
                cons_splus1, params) - savings_ut
    # Labor leisure euler equations
    income_s = (r_s * b_s + w_s * e_s * n_s) * factor
    deriv_laborleisure = 1 - tau_payroll - tax.tau_income(
        r_s, b_s, w_s, e_s, n_s, factor, params) - tax.tau_income_deriv(
            r_s, b_s, w_s, e_s, n_s, factor, params) * income_s
    error2 = household.marg_ut_cons(cons_s, params) * w_s * e[
        -(length):, j] * deriv_laborleisure - household.marg_ut_labor(
            n_s, chi_n[-length:], params)
    # Check and punish constraint violations
    mask1 = n_guess < 0
    error2[mask1] += 1e12
    mask2 = n_guess > ltilde
    error2[mask2] += 1e12
    mask3 = cons_s < 0
    error2[mask3] += 1e12
    mask4 = b_guess <= 0
    error2[mask4] += 1e12
    mask5 = cons_splus1 < 0
    error2[mask5] += 1e12
    return list(error1.flatten()) + list(error2.flatten())
Example #3
0
def Steady_state_TPI_solver(guesses, winit, rinit, BQinit, T_H_init, factor, j, s, t, params, theta, tau_bq, rho, lambdas, e, initial_b, chi_b, chi_n):
    '''
    Parameters:
        guesses = distribution of capital and labor (various length list)
        winit   = wage rate ((T+S)x1 array)
        rinit   = rental rate ((T+S)x1 array)
        BQinit = aggregate bequests ((T+S)x1 array)
        T_H_init = lump sum tax over time ((T+S)x1 array)
        factor = scaling factor (scalar)
        j = which ability type is being solved for (scalar)
        s = which upper triangle loop is being solved for (scalar)
        t = which diagonal is being solved for (scalar)
        params = list of parameters (list)
        theta = replacement rates (Jx1 array)
        tau_bq = bequest tax rate (Jx1 array)
        rho = mortalit rate (Sx1 array)
        lambdas = ability weights (Jx1 array)
        e = ability type (SxJ array)
        initial_b = capital stock distribution in period 0 (SxJ array)
        chi_b = chi^b_j (Jx1 array)
        chi_n = chi^n_s (Sx1 array)
    Output:
        Value of Euler error (various length list)
    '''

    J, S, T, beta, sigma, alpha, Z, delta, ltilde, nu, g_y, g_n_ss, tau_payroll, retire, mean_income_data, \
        a_tax_income, b_tax_income, c_tax_income, d_tax_income, h_wealth, p_wealth, m_wealth, b_ellipse, upsilon = params
    length = len(guesses) / 2
    b_guess = np.array(guesses[:length])
    n_guess = np.array(guesses[length:])

    if length == S:
        b_s = np.array([0] + list(b_guess[:-1]))
    else:
        b_s = np.array([(initial_b[-(s + 3), j])] + list(b_guess[:-1]))
    b_splus1 = b_guess
    b_splus2 = np.array(list(b_guess[1:]) + [0])
    w_s = winit[t:t + length]
    w_splus1 = winit[t + 1:t + length + 1]
    r_s = rinit[t:t + length]
    r_splus1 = rinit[t + 1:t + length + 1]
    n_s = n_guess
    n_extended = np.array(list(n_guess[1:]) + [0])
    e_s = e[-length:, j]
    e_extended = np.array(list(e[-length + 1:, j]) + [0])
    BQ_s = BQinit[t:t + length]
    BQ_splus1 = BQinit[t + 1:t + length + 1]
    T_H_s = T_H_init[t:t + length]
    T_H_splus1 = T_H_init[t + 1:t + length + 1]
    # Savings euler equations
    tax_s = tax.total_taxes(r_s, b_s, w_s, e_s, n_s, BQ_s, lambdas[
                            j], factor, T_H_s, j, 'TPI', False, params, theta, tau_bq)
    tax_splus1 = tax.total_taxes(r_splus1, b_splus1, w_splus1, e_extended, n_extended, BQ_splus1, lambdas[
                                 j], factor, T_H_splus1, j, 'TPI', True, params, theta, tau_bq)
    cons_s = household.get_cons(r_s, b_s, w_s, e_s, n_s, BQ_s, lambdas[
                                j], b_splus1, params, tax_s)
    cons_splus1 = household.get_cons(r_splus1, b_splus1, w_splus1, e_extended, n_extended, BQ_splus1, lambdas[
                                     j], b_splus2, params, tax_splus1)
    income_splus1 = (r_splus1 * b_splus1 + w_splus1 *
                     e_extended * n_extended) * factor
    savings_ut = rho[-(length):] * np.exp(-sigma * g_y) * \
        chi_b[-(length):, j] * b_splus1 ** (-sigma)
    deriv_savings = 1 + r_splus1 * (1 - tax.tau_income(
        r_splus1, b_splus1, w_splus1, e_extended, n_extended, factor, params) - tax.tau_income_deriv(
        r_splus1, b_splus1, w_splus1, e_extended, n_extended, factor, params) * income_splus1) - tax.tau_w_prime(
        b_splus1, params) * b_splus1 - tax.tau_wealth(b_splus1, params)
    error1 = household.marg_ut_cons(cons_s, params) - beta * (1 - rho[-(length):]) * np.exp(-sigma * g_y) * deriv_savings * household.marg_ut_cons(
        cons_splus1, params) - savings_ut
    # Labor leisure euler equations
    income_s = (r_s * b_s + w_s * e_s * n_s) * factor
    deriv_laborleisure = 1 - tau_payroll - tax.tau_income(r_s, b_s, w_s, e_s, n_s, factor, params) - tax.tau_income_deriv(
        r_s, b_s, w_s, e_s, n_s, factor, params) * income_s
    error2 = household.marg_ut_cons(cons_s, params) * w_s * e[-(
        length):, j] * deriv_laborleisure - household.marg_ut_labor(n_s, chi_n[-length:], params)
    # Check and punish constraint violations
    mask1 = n_guess < 0
    error2[mask1] += 1e12
    mask2 = n_guess > ltilde
    error2[mask2] += 1e12
    mask3 = cons_s < 0
    error2[mask3] += 1e12
    mask4 = b_guess <= 0
    error2[mask4] += 1e12
    mask5 = cons_splus1 < 0
    error2[mask5] += 1e12
    return list(error1.flatten()) + list(error2.flatten())
Example #4
0
def euler_savings_func(
    w, r, e, n_guess, b_s, b_splus1, b_splus2, BQ, factor, T_H, chi_b, params, theta, tau_bq, rho, lambdas
):
    """
    This function is usually looped through over J, so it does one ability group at a time.
    Inputs:
        w = wage rate (scalar)
        r = rental rate (scalar)
        e = ability levels (Sx1 array)
        n_guess = labor distribution (Sx1 array)
        b_s = wealth holdings at the start of a period (Sx1 array)
        b_splus1 = wealth holdings for the next period (Sx1 array)
        b_splus2 = wealth holdings for 2 periods ahead (Sx1 array)
        BQ = aggregate bequests for a certain ability (scalar)
        factor = scaling factor to convert to dollars (scalar)
        T_H = lump sum tax (scalar)
        chi_b = chi^b_j for a certain ability (scalar)
        params = parameter list (list)
        theta = replacement rate for a certain ability (scalar)
        tau_bq = bequest tax rate (scalar)
        rho = mortality rate (Sx1 array)
        lambdas = ability weight (scalar)
    Output:
        euler = Value of savings euler error (Sx1 array)
    """
    J, S, T, beta, sigma, alpha, Z, delta, ltilde, nu, g_y, g_n_ss, tau_payroll, retire, mean_income_data, a_tax_income, b_tax_income, c_tax_income, d_tax_income, h_wealth, p_wealth, m_wealth, b_ellipse, upsilon = (
        params
    )
    # In order to not have 2 savings euler equations (one that solves the first S-1 equations, and one that solves the last one),
    # we combine them.  In order to do this, we have to compute a consumption term in period t+1, which requires us to have a shifted
    # e and n matrix.  We append a zero on the end of both of these so they will be the right size.  We could append any value to them,
    # since in the euler equation, the coefficient on the marginal utility of
    # consumption for this term will be zero (since rho is one).
    e_extended = np.array(list(e) + [0])
    n_extended = np.array(list(n_guess) + [0])
    tax1 = tax.total_taxes(r, b_s, w, e, n_guess, BQ, lambdas, factor, T_H, None, "SS", False, params, theta, tau_bq)
    tax2 = tax.total_taxes(
        r,
        b_splus1,
        w,
        e_extended[1:],
        n_extended[1:],
        BQ,
        lambdas,
        factor,
        T_H,
        None,
        "SS",
        True,
        params,
        theta,
        tau_bq,
    )
    cons1 = get_cons(r, b_s, w, e, n_guess, BQ, lambdas, b_splus1, params, tax1)
    cons2 = get_cons(r, b_splus1, w, e_extended[1:], n_extended[1:], BQ, lambdas, b_splus2, params, tax2)
    income = (r * b_splus1 + w * e_extended[1:] * n_extended[1:]) * factor
    deriv = (
        1
        + r
        * (
            1
            - tax.tau_income(r, b_splus1, w, e_extended[1:], n_extended[1:], factor, params)
            - tax.tau_income_deriv(r, b_splus1, w, e_extended[1:], n_extended[1:], factor, params) * income
        )
        - tax.tau_w_prime(b_splus1, params) * b_splus1
        - tax.tau_wealth(b_splus1, params)
    )
    savings_ut = rho * np.exp(-sigma * g_y) * chi_b * b_splus1 ** (-sigma)
    # Again, not who in this equation, the (1-rho) term will zero out in the last period, so the last entry of cons2 can be complete
    # gibberish (which it is).  It just has to exist so cons2 is the right
    # size to match all other arrays in the equation.
    euler = (
        marg_ut_cons(cons1, params)
        - beta * (1 - rho) * deriv * marg_ut_cons(cons2, params) * np.exp(-sigma * g_y)
        - savings_ut
    )
    return euler
Example #5
0
def FOC_savings(r, w, b, b_splus1, b_splus2, n, BQ, factor, T_H,
                params):
    '''
    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.

    Inputs:
        r           = scalar, interest rate
        w           = scalar, wage rate
        b           = [S,J] array, distribution of wealth/capital
        b_splus1    = [S,J] array, distribution of wealth/capital,
                        one period ahead
        b_splus2    = [S,J] array, distribution of wealth/capital, two
                        periods ahead
        n           = [S,J] array, distribution of labor supply
        BQ          = [J,] vector, aggregate bequests by lifetime income
                        group
        factor      = scalar, scaling factor to convert model income to
                        dollars
        T_H         = scalar, lump sum transfer
        params      = length 18 tuple (e, sigma, beta, g_y, chi_b,
                                       theta, tau_bq, rho, lambdas, J,
                                       S, etr_params, mtry_params,
                                       h_wealth, p_wealth, m_wealth,
                                       tau_payroll, tau_bq)
        e           = [S,J] array, effective labor units
        sigma       = scalar, coefficient of relative risk aversion
        beta        = scalar, discount factor
        g_y         = scalar, exogenous labor augmenting technological
                        growth
        chi_b       = [J,] vector, utility weight on bequests for each
                        lifetime income group
        theta       = [J,] vector, replacement rate for each lifetime
                        income group
        tau_bq      = scalar, bequest tax rate (scalar)
        rho         = [S,] vector, mortality rates
        lambdas     = [J,] vector, ability weights
        J           = integer, number of lifetime income groups
        S           = integer, number of economically active periods in
                        lifetime
        etr_params  = [S,12] array, parameters of effective income tax
                        rate function
        mtry_params = [S,12] array, parameters of marginal tax rate on
                        capital income function
        h_wealth    = scalar, parameter in wealth tax function
        p_wealth    = scalar, parameter in wealth tax function
        m_wealth    = scalar, parameter in wealth tax function
        tau_payroll = scalar, payroll tax rate
        tau_bq      = scalar, bequest tax rate

    Functions called:
        get_cons
        marg_ut_cons
        tax.total_taxes
        tax.MTR_capital

    Objects in function:
        tax1 = [S,J] array, net taxes in the current period
        tax2 = [S,J] array, net taxes one period ahead
        cons1 = [S,J] array, consumption in the current period
        cons2 = [S,J] array, consumption one period ahead
        deriv = [S,J] array, after-tax return on capital
        savings_ut = [S,J] array, marginal utility from savings
        euler = [S,J] array, Euler error from FOC for savings

    Returns: euler
    '''
    (e, sigma, beta, g_y, chi_b, theta, tau_bq, rho, lambdas, j, J, S,
     analytical_mtrs, etr_params, mtry_params, h_wealth, p_wealth,
     m_wealth, tau_payroll, retire, method) = params

    # In order to not have 2 savings euler equations (one that solves
    # the first S-1 equations, and one that solves the last one), we
    # combine them.  In order to do this, we have to compute a
    # consumption term in period t+1, which requires us to have a shifted
    # e and n matrix.  We append a zero on the end of both of these so
    # they will be the right size.  We could append any value to them,
    # since in the euler equation, the coefficient on the marginal
    # utility of consumption for this term will be zero (since rho is
    # one).
    e_extended = np.array(list(e) + [0])
    n_extended = np.array(list(n) + [0])
    etr_params_extended = np.append(etr_params,
                                    np.reshape(etr_params[-1, :],
                                               (1, etr_params.shape[1])),
                                    axis=0)[1:, :]
    mtry_params_extended = np.append(mtry_params,
                                     np.reshape(mtry_params[-1, :],
                                                (1, mtry_params.shape[1])),
                                     axis=0)[1:, :]
    if method == 'TPI':
        r_extended = np.append(r, r[-1])
        w_extended = np.append(w, w[-1])
        BQ_extended = np.append(BQ, BQ[-1])
        T_H_extended = np.append(T_H, T_H[-1])
    elif method == 'SS':
        r_extended = np.array([r, r])
        w_extended = np.array([w, w])
        BQ_extended = np.array([BQ, BQ])
        T_H_extended = np.array([T_H, T_H])

    tax1_params = (e, lambdas, method, retire, etr_params, h_wealth,
                   p_wealth, m_wealth, tau_payroll, theta, tau_bq, J, S)
    tax1 = tax.total_taxes(r, w, b, n, BQ, factor, T_H, j, False,
                           tax1_params)
    tax2_params = (e_extended[1:], lambdas, method, retire,
                   etr_params_extended, h_wealth, p_wealth, m_wealth,
                   tau_payroll, theta, tau_bq, J, S)
    tax2 = tax.total_taxes(r_extended[1:], w_extended[1:], b_splus1,
                           n_extended[1:], BQ_extended[1:], factor,
                           T_H_extended[1:], j, True, tax2_params)
    cons1_params = (e, lambdas, g_y)
    cons1 = get_cons(r, w, b, b_splus1, n, BQ, tax1, cons1_params)
    cons2_params = (e_extended[1:], lambdas, g_y)
    cons2 = get_cons(r_extended[1:], w_extended[1:], b_splus1, b_splus2,
                     n_extended[1:], BQ_extended[1:], tax2,
                     cons2_params)
    cons2[-1] = 0.01  # set to small positive number to avoid exception
    # errors when negative b/c this period value doesn't matter -
    # it's consumption after the last period of life
    mtr_cap_params = (e_extended[1:], etr_params_extended,
                      mtry_params_extended, analytical_mtrs)
    deriv = ((1 + r_extended[1:]) - r_extended[1:] *
             (tax.MTR_capital(r_extended[1:], w_extended[1:], b_splus1,
                              n_extended[1:], factor, mtr_cap_params)) -
             (tax.tau_w_prime(b_splus1, (h_wealth, p_wealth, m_wealth)) *
             b_splus1) - tax.tau_wealth(b_splus1, (h_wealth, p_wealth,
                                                   m_wealth)))

    savings_ut = (rho * np.exp(-sigma * g_y) * chi_b * b_splus1 **
                  (-sigma))

    euler_error = (marg_ut_cons(cons1, sigma) - beta * (1 - rho) *
                   deriv * marg_ut_cons(cons2, sigma) *
                   np.exp(-sigma * g_y) - savings_ut)

    return euler_error
Example #6
0
def run_TPI(income_tax_params,
            tpi_params,
            iterative_params,
            initial_values,
            SS_values,
            fix_transfers=False,
            output_dir="./OUTPUT"):

    # unpack tuples of parameters
    analytical_mtrs, etr_params, mtrx_params, mtry_params = income_tax_params
    maxiter, mindist_SS, mindist_TPI = iterative_params
    J, S, T, BW, beta, sigma, alpha, Z, delta, ltilde, nu, g_y,\
                  g_n_vector, tau_payroll, tau_bq, rho, omega, N_tilde, lambdas, imm_rates, e, retire, mean_income_data,\
                  factor, T_H_baseline, h_wealth, p_wealth, m_wealth, b_ellipse, upsilon, chi_b, chi_n, theta = tpi_params
    K0, b_sinit, b_splus1init, factor, initial_b, initial_n, omega_S_preTP = initial_values
    Kss, Lss, rss, wss, BQss, T_Hss, Gss, bssmat_splus1, nssmat = SS_values

    TPI_FIG_DIR = output_dir
    # Initialize guesses at time paths
    domain = np.linspace(0, T, T)
    r = np.ones(T + S) * rss
    BQ = np.zeros((T + S, J))
    BQ0_params = (omega_S_preTP.reshape(S, 1), lambdas, rho.reshape(S, 1),
                  g_n_vector[0], 'SS')
    BQ0 = household.get_BQ(r[0], initial_b, BQ0_params)
    for j in xrange(J):
        BQ[:, j] = list(np.linspace(BQ0[j], BQss[j], T)) + [BQss[j]] * S
    BQ = np.array(BQ)
    # print "BQ values = ", BQ[0, :], BQ[100, :], BQ[-1, :], BQss
    # print "K0 vs Kss = ", K0-Kss

    if fix_transfers:
        T_H = T_H_baseline
    else:
        if np.abs(T_Hss) < 1e-13:
            T_Hss2 = 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:
            T_Hss2 = T_Hss
        T_H = np.ones(T + S) * T_Hss2 * (r / rss)
    G = np.ones(T + S) * Gss
    # # print "T_H values = ", T_H[0], T_H[100], T_H[-1], T_Hss
    # # print "omega diffs = ", (omega_S_preTP-omega[-1]).max(), (omega[10]-omega[-1]).max()
    #
    # Make array of initial guesses for labor supply and savings
    domain2 = np.tile(domain.reshape(T, 1, 1), (1, S, J))
    ending_b = bssmat_splus1
    guesses_b = (-1 / (domain2 + 1)) * (ending_b - initial_b) + ending_b
    ending_b_tail = np.tile(ending_b.reshape(1, S, J), (S, 1, 1))
    guesses_b = np.append(guesses_b, ending_b_tail, axis=0)
    # print 'diff btwn start and end b: ', (guesses_b[0]-guesses_b[-1]).max()
    #
    domain3 = np.tile(np.linspace(0, 1, T).reshape(T, 1, 1), (1, S, J))
    guesses_n = domain3 * (nssmat - initial_n) + initial_n
    ending_n_tail = np.tile(nssmat.reshape(1, S, J), (S, 1, 1))
    guesses_n = np.append(guesses_n, ending_n_tail, axis=0)
    # b_mat = np.zeros((T + S, S, J))
    # n_mat = np.zeros((T + S, S, J))
    ind = np.arange(S)
    # # print 'diff btwn start and end n: ', (guesses_n[0]-guesses_n[-1]).max()
    #
    # # find economic aggregates
    K = np.zeros(T + S)
    L = np.zeros(T + S)
    K[0] = K0
    K_params = (omega[:T - 1].reshape(T - 1, S, 1), lambdas.reshape(1, 1, J),
                imm_rates[:T - 1].reshape(T - 1, S, 1), g_n_vector[1:T], 'TPI')
    K[1:T] = household.get_K(guesses_b[:T - 1], K_params)
    K[T:] = Kss
    L_params = (e.reshape(1, S, J), omega[:T, :].reshape(T, S, 1),
                lambdas.reshape(1, 1, J), 'TPI')
    L[:T] = firm.get_L(guesses_n[:T], L_params)
    L[T:] = Lss
    Y_params = (alpha, Z)
    Y = firm.get_Y(K, L, Y_params)
    r_params = (alpha, delta)
    r[:T] = firm.get_r(Y[:T], K[:T], r_params)

    # uncomment lines below if want to use starting values from prior run
    r = TPI_START_VALUES['r']
    K = TPI_START_VALUES['K']
    L = TPI_START_VALUES['L']
    Y = TPI_START_VALUES['Y']
    T_H = TPI_START_VALUES['T_H']
    BQ = TPI_START_VALUES['BQ']
    G = TPI_START_VALUES['G']

    guesses_b = TPI_START_VALUES['b_mat']
    guesses_n = TPI_START_VALUES['n_mat']

    TPIiter = 0
    TPIdist = 10
    PLOT_TPI = False

    euler_errors = np.zeros((T, 2 * S, J))
    TPIdist_vec = np.zeros(maxiter)

    # print 'analytical mtrs in tpi = ', analytical_mtrs

    while (TPIiter < maxiter) and (TPIdist >= mindist_TPI):
        # Plot TPI for K for each iteration, so we can see if there is a
        # problem
        if PLOT_TPI is True:
            K_plot = list(K) + list(np.ones(10) * Kss)
            L_plot = list(L) + list(np.ones(10) * Lss)
            plt.figure()
            plt.axhline(y=Kss,
                        color='black',
                        linewidth=2,
                        label=r"Steady State $\hat{K}$",
                        ls='--')
            plt.plot(np.arange(T + 10),
                     Kpath_plot[:T + 10],
                     'b',
                     linewidth=2,
                     label=r"TPI time path $\hat{K}_t$")
            plt.savefig(os.path.join(TPI_FIG_DIR, "TPI_K"))

        guesses = (guesses_b, guesses_n)
        w_params = (Z, alpha, delta)
        w = firm.get_w_from_r(r, w_params)
        # print 'r and rss diff = ', r-rss
        # print 'w and wss diff = ', w-wss
        # print 'BQ and BQss diff = ', BQ-BQss
        # print 'T_H and T_Hss diff = ', T_H - T_Hss
        # print 'guess b and bss = ', (bssmat_splus1 - guesses_b).max()
        # print 'guess n and nss = ', (nssmat - guesses_n).max()
        outer_loop_vars = (r, w, BQ, T_H)
        inner_loop_params = (income_tax_params, tpi_params, initial_values,
                             ind)

        # Solve HH problem in inner loop
        euler_errors, b_mat, n_mat = inner_loop(guesses, outer_loop_vars,
                                                inner_loop_params)

        # print 'guess b and bss = ', (b_mat - guesses_b).max()
        # print 'guess n and nss over time = ', (n_mat - guesses_n).max(axis=2).max(axis=1)
        # print 'guess n and nss over age = ', (n_mat - guesses_n).max(axis=0).max(axis=1)
        # print 'guess n and nss over ability = ', (n_mat - guesses_n).max(axis=0).max(axis=0)
        # quit()

        print 'Max Euler error: ', (np.abs(euler_errors)).max()

        bmat_s = np.zeros((T, S, J))
        bmat_s[0, 1:, :] = initial_b[:-1, :]
        bmat_s[1:, 1:, :] = b_mat[:T - 1, :-1, :]
        bmat_splus1 = np.zeros((T, S, J))
        bmat_splus1[:, :, :] = b_mat[:T, :, :]

        K[0] = K0
        K_params = (omega[:T - 1].reshape(T - 1, S,
                                          1), lambdas.reshape(1, 1, J),
                    imm_rates[:T - 1].reshape(T - 1, S,
                                              1), g_n_vector[1:T], 'TPI')
        K[1:T] = household.get_K(bmat_splus1[:T - 1], K_params)
        L_params = (e.reshape(1, S, J), omega[:T, :].reshape(T, S, 1),
                    lambdas.reshape(1, 1, J), 'TPI')
        L[:T] = firm.get_L(n_mat[:T], L_params)
        # print 'K diffs = ', K-K0
        # print 'L diffs = ', L-L[0]

        Y_params = (alpha, Z)
        Ynew = firm.get_Y(K[:T], L[:T], Y_params)
        r_params = (alpha, delta)
        rnew = firm.get_r(Ynew[:T], K[:T], r_params)
        wnew = firm.get_w_from_r(rnew, w_params)

        omega_shift = np.append(omega_S_preTP.reshape(1, S),
                                omega[:T - 1, :],
                                axis=0)
        BQ_params = (omega_shift.reshape(T, S, 1), lambdas.reshape(1, 1, J),
                     rho.reshape(1, S, 1), g_n_vector[:T].reshape(T, 1), 'TPI')
        # b_mat_shift = np.append(np.reshape(initial_b, (1, S, J)),
        #                         b_mat[:T-1, :, :], axis=0)
        b_mat_shift = bmat_splus1[:T, :, :]
        # print 'b diffs = ', (bmat_splus1[100, :, :] - initial_b).max(), (bmat_splus1[0, :, :] - initial_b).max(), (bmat_splus1[1, :, :] - initial_b).max()
        # print 'r diffs = ', rnew[1]-r[1], rnew[100]-r[100], rnew[-1]-r[-1]
        BQnew = household.get_BQ(rnew[:T].reshape(T, 1), b_mat_shift,
                                 BQ_params)
        BQss2 = np.empty(J)
        for j in range(J):
            BQss_params = (omega[1, :], lambdas[j], rho, g_n_vector[1], 'SS')
            BQss2[j] = household.get_BQ(rnew[1], bmat_splus1[1, :, j],
                                        BQss_params)
        # print 'BQ test = ', BQss2-BQss, BQss-BQnew[1], BQss-BQnew[100], BQss-BQnew[-1]

        total_tax_params = np.zeros((T, S, J, etr_params.shape[2]))
        for i in range(etr_params.shape[2]):
            total_tax_params[:, :, :, i] = np.tile(
                np.reshape(np.transpose(etr_params[:, :T, i]), (T, S, 1)),
                (1, 1, J))

        tax_receipt_params = (np.tile(e.reshape(1, S, J), (T, 1, 1)),
                              lambdas.reshape(1, 1,
                                              J), omega[:T].reshape(T, S,
                                                                    1), 'TPI',
                              total_tax_params, theta, tau_bq, tau_payroll,
                              h_wealth, p_wealth, m_wealth, retire, T, S, J)
        net_tax_receipts = np.array(
            list(
                tax.get_lump_sum(np.tile(rnew[:T].reshape(T, 1, 1), (
                    1, S, J)), np.tile(wnew[:T].reshape(T, 1, 1), (
                        1, S, J)), bmat_s, n_mat[:T, :, :], BQnew[:T].reshape(
                            T, 1, J), factor, tax_receipt_params)) +
            [T_Hss] * S)

        r[:T] = utils.convex_combo(rnew[:T], r[:T], nu)
        BQ[:T] = utils.convex_combo(BQnew[:T], BQ[:T], nu)
        if fix_transfers:
            T_H_new = T_H
            G[:T] = net_tax_receipts[:T] - T_H[:T]
        else:
            T_H_new = net_tax_receipts
            T_H[:T] = utils.convex_combo(T_H_new[:T], T_H[:T], nu)
            G[:T] = 0.0

        etr_params_path = np.zeros((T, S, J, etr_params.shape[2]))
        for i in range(etr_params.shape[2]):
            etr_params_path[:, :, :, i] = np.tile(
                np.reshape(np.transpose(etr_params[:, :T, i]), (T, S, 1)),
                (1, 1, J))
        tax_path_params = (np.tile(e.reshape(1, S, J), (T, 1, 1)), lambdas,
                           'TPI', retire, etr_params_path, h_wealth, p_wealth,
                           m_wealth, tau_payroll, theta, tau_bq, J, S)
        b_to_use = np.zeros((T, S, J))
        b_to_use[0, 1:, :] = initial_b[:-1, :]
        b_to_use[1:, 1:, :] = b_mat[:T - 1, :-1, :]
        tax_path = tax.total_taxes(np.tile(r[:T].reshape(T, 1, 1), (1, S, J)),
                                   np.tile(w[:T].reshape(T, 1, 1),
                                           (1, S, J)), b_to_use,
                                   n_mat[:T, :, :], BQ[:T, :].reshape(T, 1, J),
                                   factor, T_H[:T].reshape(T, 1, 1), None,
                                   False, tax_path_params)

        y_path = (np.tile(r[:T].reshape(T, 1, 1),
                          (1, S, J)) * b_to_use[:T, :, :] +
                  np.tile(w[:T].reshape(T, 1, 1),
                          (1, S, J)) * np.tile(e.reshape(1, S, J),
                                               (T, 1, 1)) * n_mat[:T, :, :])
        cons_params = (e.reshape(1, S, J), lambdas.reshape(1, 1, J), g_y)
        c_path = household.get_cons(r[:T].reshape(T, 1,
                                                  1), w[:T].reshape(T, 1, 1),
                                    b_to_use[:T, :, :], b_mat[:T, :, :],
                                    n_mat[:T, :, :], BQ[:T].reshape(T, 1, J),
                                    tax_path, cons_params)

        guesses_b = utils.convex_combo(b_mat, guesses_b, nu)
        guesses_n = utils.convex_combo(n_mat, guesses_n, nu)
        if T_H.all() != 0:
            TPIdist = np.array(
                list(utils.pct_diff_func(rnew[:T], r[:T])) +
                list(utils.pct_diff_func(BQnew[:T], BQ[:T]).flatten()) +
                list(utils.pct_diff_func(T_H_new[:T], T_H[:T]))).max()
            print 'r dist = ', np.array(
                list(utils.pct_diff_func(rnew[:T], r[:T]))).max()
            print 'BQ dist = ', np.array(
                list(utils.pct_diff_func(BQnew[:T], BQ[:T]).flatten())).max()
            print 'T_H dist = ', np.array(
                list(utils.pct_diff_func(T_H_new[:T], T_H[:T]))).max()
            print 'T_H path = ', T_H[:20]
            # print 'r old = ', r[:T]
            # print 'r new = ', rnew[:T]
            # print 'K old = ', K[:T]
            # print 'L old = ', L[:T]
            # print 'income = ', y_path[:, :, -1]
            # print 'taxes = ', tax_path[:, :, -1]
            # print 'labor supply = ', n_mat[:, :, -1]
            # print 'max and min labor = ', n_mat.max(), n_mat.min()
            # print 'max and min labor = ', np.argmax(n_mat), np.argmin(n_mat)
            # print 'max and min labor, j = 7 = ', n_mat[:,:,-1].max(), n_mat[:,:,-1].min()
            # print 'max and min labor, j = 6 = ', n_mat[:,:,-2].max(), n_mat[:,:,-2].min()
            # print 'max and min labor, j = 5 = ', n_mat[:,:,4].max(), n_mat[:,:,4].min()
            # print 'max and min labor, j = 4 = ', n_mat[:,:,3].max(), n_mat[:,:,3].min()
            # print 'max and min labor, j = 3 = ', n_mat[:,:,2].max(), n_mat[:,:,2].min()
            # print 'max and min labor, j = 2 = ', n_mat[:,:,1].max(), n_mat[:,:,1].min()
            # print 'max and min labor, j = 1 = ', n_mat[:,:,0].max(), n_mat[:,:,0].min()
            # print 'max and min labor, S = 80 = ', n_mat[:,-1,-1].max(), n_mat[:,-1,-1].min()
            # print "number  > 1 = ", (n_mat > 1).sum()
            # print "number  < 0, = ", (n_mat < 0).sum()
            # print "number  > 1, j=7 = ", (n_mat[:T,:,-1] > 1).sum()
            # print "number  < 0, j=7 = ", (n_mat[:T,:,-1] < 0).sum()
            # print "number  > 1, s=80, j=7 = ", (n_mat[:T,-1,-1] > 1).sum()
            # print "number  < 0, s=80, j=7 = ", (n_mat[:T,-1,-1] < 0).sum()
            # print "number  > 1, j= 7, age 80= ", (n_mat[:T,-1,-1] > 1).sum()
            # print "number  < 0, j = 7, age 80= ", (n_mat[:T,-1,-1] < 0).sum()
            # print "number  > 1, j= 7, age 80, period 0 to 10= ", (n_mat[:30,-1,-1] > 1).sum()
            # print "number  < 0, j = 7, age 80, period 0 to 10= ", (n_mat[:30,-1,-1] < 0).sum()
            # print "number  > 1, j= 7, age 70-79, period 0 to 10= ", (n_mat[:30,70:80,-1] > 1).sum()
            # print "number  < 0, j = 7, age 70-79, period 0 to 10= ", (n_mat[:30,70:80   ,-1] < 0).sum()
            # diag_dict = {'n_mat': n_mat, 'b_mat': b_mat, 'y_path': y_path, 'c_path': c_path}
            # pickle.dump(diag_dict, open('tpi_iter1.pkl', 'wb'))

        else:
            TPIdist = np.array(
                list(utils.pct_diff_func(rnew[:T], r[:T])) +
                list(utils.pct_diff_func(BQnew[:T], BQ[:T]).flatten()) +
                list(np.abs(T_H_new[:T] - T_H[: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 '\tIteration:', TPIiter
        print '\t\tDistance:', TPIdist

    Y[:T] = Ynew

    # Solve HH problem in inner loop
    guesses = (guesses_b, guesses_n)
    outer_loop_vars = (r, w, BQ, T_H)
    inner_loop_params = (income_tax_params, tpi_params, initial_values, ind)
    euler_errors, b_mat, n_mat = inner_loop(guesses, outer_loop_vars,
                                            inner_loop_params)

    bmat_s = np.zeros((T, S, J))
    bmat_s[0, 1:, :] = initial_b[:-1, :]
    bmat_s[1:, 1:, :] = b_mat[:T - 1, :-1, :]
    bmat_splus1 = np.zeros((T, S, J))
    bmat_splus1[:, :, :] = b_mat[:T, :, :]

    K[0] = K0
    K_params = (omega[:T - 1].reshape(T - 1, S, 1), lambdas.reshape(1, 1, J),
                imm_rates[:T - 1].reshape(T - 1, S, 1), g_n_vector[1:T], 'TPI')
    K[1:T] = household.get_K(bmat_splus1[:T - 1], K_params)
    L_params = (e.reshape(1, S, J), omega[:T, :].reshape(T, S, 1),
                lambdas.reshape(1, 1, J), 'TPI')
    L[:T] = firm.get_L(n_mat[:T], L_params)

    Y_params = (alpha, Z)
    Ynew = firm.get_Y(K[:T], L[:T], Y_params)
    r_params = (alpha, delta)
    rnew = firm.get_r(Ynew[:T], K[:T], r_params)
    wnew = firm.get_w_from_r(rnew, w_params)

    omega_shift = np.append(omega_S_preTP.reshape(1, S),
                            omega[:T - 1, :],
                            axis=0)
    BQ_params = (omega_shift.reshape(T, S, 1), lambdas.reshape(1, 1, J),
                 rho.reshape(1, S, 1), g_n_vector[:T].reshape(T, 1), 'TPI')
    b_mat_shift = np.append(np.reshape(initial_b, (1, S, J)),
                            b_mat[:T - 1, :, :],
                            axis=0)
    BQnew = household.get_BQ(rnew[:T].reshape(T, 1), b_mat_shift, BQ_params)

    total_tax_params = np.zeros((T, S, J, etr_params.shape[2]))
    for i in range(etr_params.shape[2]):
        total_tax_params[:, :, :, i] = np.tile(
            np.reshape(np.transpose(etr_params[:, :T, i]), (T, S, 1)),
            (1, 1, J))

    tax_receipt_params = (np.tile(e.reshape(1, S, J), (T, 1, 1)),
                          lambdas.reshape(1, 1, J), omega[:T].reshape(T, S, 1),
                          'TPI', total_tax_params, theta, tau_bq, tau_payroll,
                          h_wealth, p_wealth, m_wealth, retire, T, S, J)
    net_tax_receipts = np.array(
        list(
            tax.get_lump_sum(np.tile(rnew[:T].reshape(T, 1, 1), (
                1, S, J)), np.tile(wnew[:T].reshape(T, 1, 1), (
                    1, S, J)), bmat_s, n_mat[:T, :, :], BQnew[:T].reshape(
                        T, 1, J), factor, tax_receipt_params)) + [T_Hss] * S)

    if fix_transfers:
        G[:T] = net_tax_receipts[:T] - T_H[:T]
    else:
        T_H[:T] = net_tax_receipts[:T]
        G[:T] = 0.0

    etr_params_path = np.zeros((T, S, J, etr_params.shape[2]))
    for i in range(etr_params.shape[2]):
        etr_params_path[:, :, :, i] = np.tile(
            np.reshape(np.transpose(etr_params[:, :T, i]), (T, S, 1)),
            (1, 1, J))
    tax_path_params = (np.tile(e.reshape(1, S, J), (T, 1, 1)), lambdas, 'TPI',
                       retire, etr_params_path, h_wealth, p_wealth, m_wealth,
                       tau_payroll, theta, tau_bq, J, S)
    tax_path = tax.total_taxes(np.tile(r[:T].reshape(T, 1, 1), (1, S, J)),
                               np.tile(w[:T].reshape(T, 1, 1),
                                       (1, S, J)), bmat_s, n_mat[:T, :, :],
                               BQ[:T, :].reshape(T, 1, J), factor,
                               T_H[:T].reshape(T, 1, 1), None, False,
                               tax_path_params)

    cons_params = (e.reshape(1, S, J), lambdas.reshape(1, 1, J), g_y)
    c_path = household.get_cons(r[:T].reshape(T, 1, 1), w[:T].reshape(T, 1, 1),
                                bmat_s, bmat_splus1, n_mat[:T, :, :],
                                BQ[:T].reshape(T, 1, J), tax_path, cons_params)
    C_params = (omega[:T].reshape(T, S, 1), lambdas, 'TPI')
    C = household.get_C(c_path, C_params)
    I_params = (delta, g_y, omega[:T].reshape(T, S, 1), lambdas,
                imm_rates[:T].reshape(T, S, 1), g_n_vector[1:T + 1], 'TPI')
    I = firm.get_I(bmat_splus1[:T], K[1:T + 1], K[:T], I_params)
    rc_error = Y[:T] - C[:T] - I[:T] - G[:T]
    print 'Resource Constraint Difference:', rc_error

    # compute utility
    u_params = (sigma, np.tile(chi_n.reshape(1, S, 1),
                               (T, 1, J)), b_ellipse, ltilde, upsilon,
                np.tile(rho.reshape(1, S, 1),
                        (T, 1, J)), np.tile(chi_b.reshape(1, 1, J), (T, S, 1)))
    utility_path = household.get_u(c_path[:T, :, :], n_mat[:T, :, :],
                                   bmat_splus1[:T, :, :], u_params)

    # compute before and after-tax income
    y_path = (np.tile(r[:T].reshape(T, 1, 1), (1, S, J)) * bmat_s[:T, :, :] +
              np.tile(w[:T].reshape(T, 1, 1),
                      (1, S, J)) * np.tile(e.reshape(1, S, J),
                                           (T, 1, 1)) * n_mat[:T, :, :])
    inctax_params = (np.tile(e.reshape(1, S, J), (T, 1, 1)), etr_params_path)
    y_aftertax_path = (y_path - tax.tau_income(
        np.tile(r[:T].reshape(T, 1, 1),
                (1, S, J)), np.tile(w[:T].reshape(T, 1, 1), (1, S, J)),
        bmat_s[:T, :, :], n_mat[:T, :, :], factor, inctax_params))

    # compute after-tax wealth
    wtax_params = (h_wealth, p_wealth, m_wealth)
    b_aftertax_path = bmat_s[:T, :, :] - tax.tau_wealth(
        bmat_s[:T, :, :], wtax_params)

    print 'Checking time path for violations of constaints.'
    for t in xrange(T):
        household.constraint_checker_TPI(b_mat[t], n_mat[t], c_path[t], t,
                                         ltilde)

    eul_savings = euler_errors[:, :S, :].max(1).max(1)
    eul_laborleisure = euler_errors[:, 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,
        'K': K,
        'L': L,
        'C': C,
        'I': I,
        'BQ': BQ,
        'G': G,
        'T_H': T_H,
        'r': r,
        'w': w,
        'b_mat': b_mat,
        'n_mat': n_mat,
        'c_path': c_path,
        'tax_path': tax_path,
        'bmat_s': bmat_s,
        'utility_path': utility_path,
        'b_aftertax_path': b_aftertax_path,
        'y_aftertax_path': y_aftertax_path,
        'y_path': y_path,
        'eul_savings': eul_savings,
        'eul_laborleisure': eul_laborleisure
    }

    macro_output = {
        'Y': Y,
        'K': K,
        'L': L,
        'C': C,
        'I': I,
        'BQ': BQ,
        'G': G,
        'T_H': T_H,
        'r': r,
        'w': w,
        'tax_path': tax_path
    }

    # if ((TPIiter >= maxiter) or (np.absolute(TPIdist) > mindist_TPI)) and ENFORCE_SOLUTION_CHECKS :
    #     raise RuntimeError("Transition path equlibrium not found")
    #
    # if ((np.any(np.absolute(rc_error) >= 1e-6))
    #     and ENFORCE_SOLUTION_CHECKS):
    #     raise RuntimeError("Transition path equlibrium not found")
    #
    # if ((np.any(np.absolute(eul_savings) >= mindist_TPI) or
    #     (np.any(np.absolute(eul_laborleisure) > mindist_TPI)))
    #     and ENFORCE_SOLUTION_CHECKS):
    #     raise RuntimeError("Transition path equlibrium not found")

    return output, macro_output
Example #7
0
def run_TPI(income_tax_params, tpi_params, iterative_params,
            initial_values, SS_values, fix_transfers=False,
            output_dir="./OUTPUT"):

    # unpack tuples of parameters
    analytical_mtrs, etr_params, mtrx_params, mtry_params = income_tax_params
    maxiter, mindist_SS, mindist_TPI = iterative_params
    J, S, T, BW, beta, sigma, alpha, Z, delta, ltilde, nu, g_y,\
                  g_n_vector, tau_payroll, tau_bq, rho, omega, N_tilde, lambdas, imm_rates, e, retire, mean_income_data,\
                  factor, T_H_baseline, h_wealth, p_wealth, m_wealth, b_ellipse, upsilon, chi_b, chi_n, theta = tpi_params
    K0, b_sinit, b_splus1init, factor, initial_b, initial_n, omega_S_preTP = initial_values
    Kss, Lss, rss, wss, BQss, T_Hss, Gss, bssmat_splus1, nssmat = SS_values


    TPI_FIG_DIR = output_dir
    # Initialize guesses at time paths
    domain = np.linspace(0, T, T)
    r = np.ones(T + S) * rss
    BQ = np.zeros((T + S, J))
    BQ0_params = (omega_S_preTP.reshape(S, 1), lambdas, rho.reshape(S, 1), g_n_vector[0], 'SS')
    BQ0 = household.get_BQ(r[0], initial_b, BQ0_params)
    for j in xrange(J):
        BQ[:, j] = list(np.linspace(BQ0[j], BQss[j], T)) + [BQss[j]] * S
    BQ = np.array(BQ)
    # print "BQ values = ", BQ[0, :], BQ[100, :], BQ[-1, :], BQss
    # print "K0 vs Kss = ", K0-Kss

    if fix_transfers:
        T_H = T_H_baseline
    else:
        if np.abs(T_Hss) < 1e-13 :
            T_Hss2 = 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:
            T_Hss2 = T_Hss
        T_H = np.ones(T + S) * T_Hss2 * (r/rss)
    G = np.ones(T + S) * Gss
    # # print "T_H values = ", T_H[0], T_H[100], T_H[-1], T_Hss
    # # print "omega diffs = ", (omega_S_preTP-omega[-1]).max(), (omega[10]-omega[-1]).max()
    #
    # Make array of initial guesses for labor supply and savings
    domain2 = np.tile(domain.reshape(T, 1, 1), (1, S, J))
    ending_b = bssmat_splus1
    guesses_b = (-1 / (domain2 + 1)) * (ending_b - initial_b) + ending_b
    ending_b_tail = np.tile(ending_b.reshape(1, S, J), (S, 1, 1))
    guesses_b = np.append(guesses_b, ending_b_tail, axis=0)
    # print 'diff btwn start and end b: ', (guesses_b[0]-guesses_b[-1]).max()
    #
    domain3 = np.tile(np.linspace(0, 1, T).reshape(T, 1, 1), (1, S, J))
    guesses_n = domain3 * (nssmat - initial_n) + initial_n
    ending_n_tail = np.tile(nssmat.reshape(1, S, J), (S, 1, 1))
    guesses_n = np.append(guesses_n, ending_n_tail, axis=0)
    # b_mat = np.zeros((T + S, S, J))
    # n_mat = np.zeros((T + S, S, J))
    ind = np.arange(S)
    # # print 'diff btwn start and end n: ', (guesses_n[0]-guesses_n[-1]).max()
    #
    # # find economic aggregates
    K = np.zeros(T+S)
    L = np.zeros(T+S)
    K[0] = K0
    K_params = (omega[:T-1].reshape(T-1, S, 1), lambdas.reshape(1, 1, J), imm_rates[:T-1].reshape(T-1,S,1), g_n_vector[1:T], 'TPI')
    K[1:T] = household.get_K(guesses_b[:T-1], K_params)
    K[T:] = Kss
    L_params = (e.reshape(1, S, J), omega[:T, :].reshape(T, S, 1), lambdas.reshape(1, 1, J), 'TPI')
    L[:T] = firm.get_L(guesses_n[:T], L_params)
    L[T:] = Lss
    Y_params = (alpha, Z)
    Y = firm.get_Y(K, L, Y_params)
    r_params = (alpha, delta)
    r[:T] = firm.get_r(Y[:T], K[:T], r_params)

    # uncomment lines below if want to use starting values from prior run
    r = TPI_START_VALUES['r']
    K = TPI_START_VALUES['K']
    L = TPI_START_VALUES['L']
    Y = TPI_START_VALUES['Y']
    T_H = TPI_START_VALUES['T_H']
    BQ = TPI_START_VALUES['BQ']
    G = TPI_START_VALUES['G']

    guesses_b = TPI_START_VALUES['b_mat']
    guesses_n = TPI_START_VALUES['n_mat']


    TPIiter = 0
    TPIdist = 10
    PLOT_TPI = False

    euler_errors = np.zeros((T, 2 * S, J))
    TPIdist_vec = np.zeros(maxiter)

    # print 'analytical mtrs in tpi = ', analytical_mtrs

    while (TPIiter < maxiter) and (TPIdist >= mindist_TPI):
        # Plot TPI for K for each iteration, so we can see if there is a
        # problem
        if PLOT_TPI is True:
            K_plot = list(K) + list(np.ones(10) * Kss)
            L_plot = list(L) + list(np.ones(10) * Lss)
            plt.figure()
            plt.axhline(
                y=Kss, color='black', linewidth=2, label=r"Steady State $\hat{K}$", ls='--')
            plt.plot(np.arange(
                T + 10), Kpath_plot[:T + 10], 'b', linewidth=2, label=r"TPI time path $\hat{K}_t$")
            plt.savefig(os.path.join(TPI_FIG_DIR, "TPI_K"))


        guesses = (guesses_b, guesses_n)
        w_params = (Z, alpha, delta)
        w = firm.get_w_from_r(r, w_params)
        # print 'r and rss diff = ', r-rss
        # print 'w and wss diff = ', w-wss
        # print 'BQ and BQss diff = ', BQ-BQss
        # print 'T_H and T_Hss diff = ', T_H - T_Hss
        # print 'guess b and bss = ', (bssmat_splus1 - guesses_b).max()
        # print 'guess n and nss = ', (nssmat - guesses_n).max()
        outer_loop_vars = (r, w, BQ, T_H)
        inner_loop_params = (income_tax_params, tpi_params, initial_values, ind)

        # Solve HH problem in inner loop
        euler_errors, b_mat, n_mat = inner_loop(guesses, outer_loop_vars, inner_loop_params)

        # print 'guess b and bss = ', (b_mat - guesses_b).max()
        # print 'guess n and nss over time = ', (n_mat - guesses_n).max(axis=2).max(axis=1)
        # print 'guess n and nss over age = ', (n_mat - guesses_n).max(axis=0).max(axis=1)
        # print 'guess n and nss over ability = ', (n_mat - guesses_n).max(axis=0).max(axis=0)
        # quit()

        print 'Max Euler error: ', (np.abs(euler_errors)).max()

        bmat_s = np.zeros((T, S, J))
        bmat_s[0, 1:, :] = initial_b[:-1, :]
        bmat_s[1:, 1:, :] = b_mat[:T-1, :-1, :]
        bmat_splus1 = np.zeros((T, S, J))
        bmat_splus1[:, :, :] = b_mat[:T, :, :]

        K[0] = K0
        K_params = (omega[:T-1].reshape(T-1, S, 1), lambdas.reshape(1, 1, J),
                    imm_rates[:T-1].reshape(T-1, S, 1), g_n_vector[1:T], 'TPI')
        K[1:T] = household.get_K(bmat_splus1[:T-1], K_params)
        L_params = (e.reshape(1, S, J), omega[:T, :].reshape(T, S, 1),
                    lambdas.reshape(1, 1, J), 'TPI')
        L[:T] = firm.get_L(n_mat[:T], L_params)
        # print 'K diffs = ', K-K0
        # print 'L diffs = ', L-L[0]

        Y_params = (alpha, Z)
        Ynew = firm.get_Y(K[:T], L[:T], Y_params)
        r_params = (alpha, delta)
        rnew = firm.get_r(Ynew[:T], K[:T], r_params)
        wnew = firm.get_w_from_r(rnew, w_params)

        omega_shift = np.append(omega_S_preTP.reshape(1, S),
                                omega[:T-1, :], axis=0)
        BQ_params = (omega_shift.reshape(T, S, 1), lambdas.reshape(1, 1, J),
                     rho.reshape(1, S, 1), g_n_vector[:T].reshape(T, 1), 'TPI')
        # b_mat_shift = np.append(np.reshape(initial_b, (1, S, J)),
        #                         b_mat[:T-1, :, :], axis=0)
        b_mat_shift = bmat_splus1[:T, :, :]
        # print 'b diffs = ', (bmat_splus1[100, :, :] - initial_b).max(), (bmat_splus1[0, :, :] - initial_b).max(), (bmat_splus1[1, :, :] - initial_b).max()
        # print 'r diffs = ', rnew[1]-r[1], rnew[100]-r[100], rnew[-1]-r[-1]
        BQnew = household.get_BQ(rnew[:T].reshape(T, 1), b_mat_shift,
                                 BQ_params)
        BQss2 = np.empty(J)
        for j in range(J):
            BQss_params = (omega[1, :], lambdas[j], rho, g_n_vector[1], 'SS')
            BQss2[j] = household.get_BQ(rnew[1], bmat_splus1[1, :, j],
                                        BQss_params)
        # print 'BQ test = ', BQss2-BQss, BQss-BQnew[1], BQss-BQnew[100], BQss-BQnew[-1]

        total_tax_params = np.zeros((T, S, J, etr_params.shape[2]))
        for i in range(etr_params.shape[2]):
            total_tax_params[:, :, :, i] = np.tile(np.reshape(np.transpose(etr_params[:,:T,i]),(T,S,1)),(1,1,J))

        tax_receipt_params = (np.tile(e.reshape(1, S, J),(T,1,1)), lambdas.reshape(1, 1, J), omega[:T].reshape(T, S, 1), 'TPI',
                total_tax_params, theta, tau_bq, tau_payroll, h_wealth, p_wealth, m_wealth, retire, T, S, J)
        net_tax_receipts = np.array(list(tax.get_lump_sum(np.tile(rnew[:T].reshape(T, 1, 1),(1,S,J)), np.tile(wnew[:T].reshape(T, 1, 1),(1,S,J)),
               bmat_s, n_mat[:T,:,:], BQnew[:T].reshape(T, 1, J), factor, tax_receipt_params)) + [T_Hss] * S)

        r[:T] = utils.convex_combo(rnew[:T], r[:T], nu)
        BQ[:T] = utils.convex_combo(BQnew[:T], BQ[:T], nu)
        if fix_transfers:
            T_H_new = T_H
            G[:T] = net_tax_receipts[:T] - T_H[:T]
        else:
            T_H_new = net_tax_receipts
            T_H[:T] = utils.convex_combo(T_H_new[:T], T_H[:T], nu)
            G[:T] = 0.0

        etr_params_path = np.zeros((T,S,J,etr_params.shape[2]))
        for i in range(etr_params.shape[2]):
            etr_params_path[:,:,:,i] = np.tile(
                np.reshape(np.transpose(etr_params[:,:T,i]),(T,S,1)),(1,1,J))
        tax_path_params = (np.tile(e.reshape(1, S, J),(T,1,1)),
                           lambdas, 'TPI', retire, etr_params_path, h_wealth,
                           p_wealth, m_wealth, tau_payroll, theta, tau_bq, J, S)
        b_to_use = np.zeros((T, S, J))
        b_to_use[0, 1:, :] = initial_b[:-1, :]
        b_to_use[1:, 1:, :] = b_mat[:T-1, :-1, :]
        tax_path = tax.total_taxes(
            np.tile(r[:T].reshape(T, 1, 1),(1,S,J)),
            np.tile(w[:T].reshape(T, 1, 1),(1,S,J)), b_to_use,
            n_mat[:T,:,:], BQ[:T, :].reshape(T, 1, J), factor,
            T_H[:T].reshape(T, 1, 1), None, False, tax_path_params)

        y_path = (np.tile(r[:T].reshape(T, 1, 1), (1, S, J)) * b_to_use[:T, :, :] +
                  np.tile(w[:T].reshape(T, 1, 1), (1, S, J)) *
                  np.tile(e.reshape(1, S, J), (T, 1, 1)) * n_mat[:T, :, :])
        cons_params = (e.reshape(1, S, J), lambdas.reshape(1, 1, J), g_y)
        c_path = household.get_cons(r[:T].reshape(T, 1, 1), w[:T].reshape(T, 1, 1), b_to_use[:T,:,:], b_mat[:T,:,:], n_mat[:T,:,:],
                       BQ[:T].reshape(T, 1, J), tax_path, cons_params)


        guesses_b = utils.convex_combo(b_mat, guesses_b, nu)
        guesses_n = utils.convex_combo(n_mat, guesses_n, nu)
        if T_H.all() != 0:
            TPIdist = np.array(list(utils.pct_diff_func(rnew[:T], r[:T])) +
                               list(utils.pct_diff_func(BQnew[:T], BQ[:T]).flatten()) +
                               list(utils.pct_diff_func(T_H_new[:T], T_H[:T]))).max()
            print 'r dist = ', np.array(list(utils.pct_diff_func(rnew[:T], r[:T]))).max()
            print 'BQ dist = ', np.array(list(utils.pct_diff_func(BQnew[:T], BQ[:T]).flatten())).max()
            print 'T_H dist = ', np.array(list(utils.pct_diff_func(T_H_new[:T], T_H[:T]))).max()
            print 'T_H path = ', T_H[:20]
            # print 'r old = ', r[:T]
            # print 'r new = ', rnew[:T]
            # print 'K old = ', K[:T]
            # print 'L old = ', L[:T]
            # print 'income = ', y_path[:, :, -1]
            # print 'taxes = ', tax_path[:, :, -1]
            # print 'labor supply = ', n_mat[:, :, -1]
            # print 'max and min labor = ', n_mat.max(), n_mat.min()
            # print 'max and min labor = ', np.argmax(n_mat), np.argmin(n_mat)
            # print 'max and min labor, j = 7 = ', n_mat[:,:,-1].max(), n_mat[:,:,-1].min()
            # print 'max and min labor, j = 6 = ', n_mat[:,:,-2].max(), n_mat[:,:,-2].min()
            # print 'max and min labor, j = 5 = ', n_mat[:,:,4].max(), n_mat[:,:,4].min()
            # print 'max and min labor, j = 4 = ', n_mat[:,:,3].max(), n_mat[:,:,3].min()
            # print 'max and min labor, j = 3 = ', n_mat[:,:,2].max(), n_mat[:,:,2].min()
            # print 'max and min labor, j = 2 = ', n_mat[:,:,1].max(), n_mat[:,:,1].min()
            # print 'max and min labor, j = 1 = ', n_mat[:,:,0].max(), n_mat[:,:,0].min()
            # print 'max and min labor, S = 80 = ', n_mat[:,-1,-1].max(), n_mat[:,-1,-1].min()
            # print "number  > 1 = ", (n_mat > 1).sum()
            # print "number  < 0, = ", (n_mat < 0).sum()
            # print "number  > 1, j=7 = ", (n_mat[:T,:,-1] > 1).sum()
            # print "number  < 0, j=7 = ", (n_mat[:T,:,-1] < 0).sum()
            # print "number  > 1, s=80, j=7 = ", (n_mat[:T,-1,-1] > 1).sum()
            # print "number  < 0, s=80, j=7 = ", (n_mat[:T,-1,-1] < 0).sum()
            # print "number  > 1, j= 7, age 80= ", (n_mat[:T,-1,-1] > 1).sum()
            # print "number  < 0, j = 7, age 80= ", (n_mat[:T,-1,-1] < 0).sum()
            # print "number  > 1, j= 7, age 80, period 0 to 10= ", (n_mat[:30,-1,-1] > 1).sum()
            # print "number  < 0, j = 7, age 80, period 0 to 10= ", (n_mat[:30,-1,-1] < 0).sum()
            # print "number  > 1, j= 7, age 70-79, period 0 to 10= ", (n_mat[:30,70:80,-1] > 1).sum()
            # print "number  < 0, j = 7, age 70-79, period 0 to 10= ", (n_mat[:30,70:80   ,-1] < 0).sum()
            # diag_dict = {'n_mat': n_mat, 'b_mat': b_mat, 'y_path': y_path, 'c_path': c_path}
            # pickle.dump(diag_dict, open('tpi_iter1.pkl', 'wb'))

        else:
            TPIdist = np.array(list(utils.pct_diff_func(rnew[:T], r[:T])) +
                               list(utils.pct_diff_func(BQnew[:T], BQ[:T]).flatten()) +
                               list(np.abs(T_H_new[:T]-T_H[: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 '\tIteration:', TPIiter
        print '\t\tDistance:', TPIdist

    Y[:T] = Ynew


    # Solve HH problem in inner loop
    guesses = (guesses_b, guesses_n)
    outer_loop_vars = (r, w, BQ, T_H)
    inner_loop_params = (income_tax_params, tpi_params, initial_values, ind)
    euler_errors, b_mat, n_mat = inner_loop(guesses, outer_loop_vars, inner_loop_params)

    bmat_s = np.zeros((T, S, J))
    bmat_s[0, 1:, :] = initial_b[:-1, :]
    bmat_s[1:, 1:, :] = b_mat[:T-1, :-1, :]
    bmat_splus1 = np.zeros((T, S, J))
    bmat_splus1[:, :, :] = b_mat[:T, :, :]

    K[0] = K0
    K_params = (omega[:T-1].reshape(T-1, S, 1), lambdas.reshape(1, 1, J), imm_rates[:T-1].reshape(T-1,S,1), g_n_vector[1:T], 'TPI')
    K[1:T] = household.get_K(bmat_splus1[:T-1], K_params)
    L_params = (e.reshape(1, S, J), omega[:T, :].reshape(T, S, 1), lambdas.reshape(1, 1, J), 'TPI')
    L[:T]  = firm.get_L(n_mat[:T], L_params)

    Y_params = (alpha, Z)
    Ynew = firm.get_Y(K[:T], L[:T], Y_params)
    r_params = (alpha, delta)
    rnew = firm.get_r(Ynew[:T], K[:T], r_params)
    wnew = firm.get_w_from_r(rnew, w_params)

    omega_shift = np.append(omega_S_preTP.reshape(1,S),omega[:T-1,:],axis=0)
    BQ_params = (omega_shift.reshape(T, S, 1), lambdas.reshape(1, 1, J), rho.reshape(1, S, 1),
                 g_n_vector[:T].reshape(T, 1), 'TPI')
    b_mat_shift = np.append(np.reshape(initial_b,(1,S,J)),b_mat[:T-1,:,:],axis=0)
    BQnew = household.get_BQ(rnew[:T].reshape(T, 1), b_mat_shift, BQ_params)

    total_tax_params = np.zeros((T,S,J,etr_params.shape[2]))
    for i in range(etr_params.shape[2]):
        total_tax_params[:,:,:,i] = np.tile(np.reshape(np.transpose(etr_params[:,:T,i]),(T,S,1)),(1,1,J))

    tax_receipt_params = (np.tile(e.reshape(1, S, J),(T,1,1)), lambdas.reshape(1, 1, J), omega[:T].reshape(T, S, 1), 'TPI',
            total_tax_params, theta, tau_bq, tau_payroll, h_wealth, p_wealth, m_wealth, retire, T, S, J)
    net_tax_receipts = np.array(list(tax.get_lump_sum(np.tile(rnew[:T].reshape(T, 1, 1),(1,S,J)), np.tile(wnew[:T].reshape(T, 1, 1),(1,S,J)),
           bmat_s, n_mat[:T,:,:], BQnew[:T].reshape(T, 1, J), factor, tax_receipt_params)) + [T_Hss] * S)

    if fix_transfers:
        G[:T] = net_tax_receipts[:T] - T_H[:T]
    else:
        T_H[:T] = net_tax_receipts[:T]
        G[:T] = 0.0

    etr_params_path = np.zeros((T,S,J,etr_params.shape[2]))
    for i in range(etr_params.shape[2]):
        etr_params_path[:,:,:,i] = np.tile(np.reshape(np.transpose(etr_params[:,:T,i]),(T,S,1)),(1,1,J))
    tax_path_params = (np.tile(e.reshape(1, S, J),(T,1,1)), lambdas, 'TPI', retire, etr_params_path, h_wealth,
                       p_wealth, m_wealth, tau_payroll, theta, tau_bq, J, S)
    tax_path = tax.total_taxes(np.tile(r[:T].reshape(T, 1, 1),(1,S,J)), np.tile(w[:T].reshape(T, 1, 1),(1,S,J)), bmat_s,
                               n_mat[:T,:,:], BQ[:T, :].reshape(T, 1, J), factor, T_H[:T].reshape(T, 1, 1), None, False, tax_path_params)

    cons_params = (e.reshape(1, S, J), lambdas.reshape(1, 1, J), g_y)
    c_path = household.get_cons(r[:T].reshape(T, 1, 1), w[:T].reshape(T, 1, 1), bmat_s, bmat_splus1, n_mat[:T,:,:],
                   BQ[:T].reshape(T, 1, J), tax_path, cons_params)
    C_params = (omega[:T].reshape(T, S, 1), lambdas, 'TPI')
    C = household.get_C(c_path, C_params)
    I_params = (delta, g_y, omega[:T].reshape(T, S, 1), lambdas, imm_rates[:T].reshape(T, S, 1), g_n_vector[1:T+1], 'TPI')
    I = firm.get_I(bmat_splus1[:T], K[1:T+1], K[:T], I_params)
    rc_error = Y[:T] - C[:T] - I[:T] - G[:T]
    print 'Resource Constraint Difference:', rc_error

    # compute utility
    u_params = (sigma, np.tile(chi_n.reshape(1, S, 1), (T, 1, J)),
                b_ellipse, ltilde, upsilon,
                np.tile(rho.reshape(1, S, 1), (T, 1, J)),
                np.tile(chi_b.reshape(1, 1, J), (T, S, 1)))
    utility_path = household.get_u(c_path[:T, :, :], n_mat[:T, :, :],
                                   bmat_splus1[:T, :, :], u_params)

    # compute before and after-tax income
    y_path = (np.tile(r[:T].reshape(T, 1, 1), (1, S, J)) * bmat_s[:T, :, :] +
              np.tile(w[:T].reshape(T, 1, 1), (1, S, J)) *
              np.tile(e.reshape(1, S, J), (T, 1, 1)) * n_mat[:T, :, :])
    inctax_params = (np.tile(e.reshape(1, S, J), (T, 1, 1)), etr_params_path)
    y_aftertax_path = (y_path -
                       tax.tau_income(np.tile(r[:T].reshape(T, 1, 1), (1, S, J)),
                                      np.tile(w[:T].reshape(T, 1, 1), (1, S, J)),
                                      bmat_s[:T,:,:], n_mat[:T,:,:], factor, inctax_params))

    # compute after-tax wealth
    wtax_params = (h_wealth, p_wealth, m_wealth)
    b_aftertax_path = bmat_s[:T,:,:] - tax.tau_wealth(bmat_s[:T,:,:], wtax_params)

    print'Checking time path for violations of constaints.'
    for t in xrange(T):
        household.constraint_checker_TPI(
            b_mat[t], n_mat[t], c_path[t], t, ltilde)

    eul_savings = euler_errors[:, :S, :].max(1).max(1)
    eul_laborleisure = euler_errors[:, 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, 'K': K, 'L': L, 'C': C, 'I': I, 'BQ': BQ, 'G': G,
              'T_H': T_H, 'r': r, 'w': w, 'b_mat': b_mat, 'n_mat': n_mat,
              'c_path': c_path, 'tax_path': tax_path, 'bmat_s': bmat_s,
              'utility_path': utility_path, 'b_aftertax_path': b_aftertax_path,
              'y_aftertax_path': y_aftertax_path, 'y_path': y_path,
              'eul_savings': eul_savings, 'eul_laborleisure': eul_laborleisure}

    macro_output = {'Y': Y, 'K': K, 'L': L, 'C': C, 'I': I,
                    'BQ': BQ, 'G': G, 'T_H': T_H, 'r': r, 'w': w,
                    'tax_path': tax_path}


    # if ((TPIiter >= maxiter) or (np.absolute(TPIdist) > mindist_TPI)) and ENFORCE_SOLUTION_CHECKS :
    #     raise RuntimeError("Transition path equlibrium not found")
    #
    # if ((np.any(np.absolute(rc_error) >= 1e-6))
    #     and ENFORCE_SOLUTION_CHECKS):
    #     raise RuntimeError("Transition path equlibrium not found")
    #
    # if ((np.any(np.absolute(eul_savings) >= mindist_TPI) or
    #     (np.any(np.absolute(eul_laborleisure) > mindist_TPI)))
    #     and ENFORCE_SOLUTION_CHECKS):
    #     raise RuntimeError("Transition path equlibrium not found")

    return output, macro_output