예제 #1
0
def euler_sys(guesses, *args):
    '''
    --------------------------------------------------------------------
    Specify the system of Euler Equations characterizing the household
    problem.
    --------------------------------------------------------------------
    INPUTS:
    guesses = (2S-1,) vector, guess at labor supply and savings decisions
    args = length 14 tuple, (r, w, beta, sigma, l_tilde, chi_n_vec,
            b_ellip, upsilon, diff, S, SS_tol)

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        hh.get_n_errors()
        hh.get_n_errors()
        hh.get_cons()

    OBJECTS CREATED WITHIN FUNCTION:
    r    = scalar > 0, guess at steady-state interest rate
    w    = scalar > 0, guess at steady-state wage
    beta       = scalar in (0,1), discount factor
    sigma      = scalar >= 1, coefficient of relative risk aversion
    l_tilde    = scalar > 0, per-period time endowment for every agent
    chi_n_vec  = (S,) vector, values for chi^n_s
    b_ellip    = scalar > 0, fitted value of b for elliptical disutility
                 of labor
    upsilon    = scalar > 1, fitted value of upsilon for elliptical
                 disutility of labor
    A          = scalar > 0, total factor productivity parameter in
                 firms' production function
    diff    = boolean, =True if simple difference Euler errors,
                 otherwise percent deviation Euler errors
    S          = integer >= 3, number of periods in individual lifetime
    SS_tol     = scalar > 0, tolerance level for steady-state fsolve
    nvec       = (S,) vector, lifetime labor supply (n1, n2, ...nS)
    bvec       = (S,) vector, lifetime savings (b1, b2, ...bS) with b1=0
    cvec       = (S,) vector, lifetime consumption (c1, c2, ...cS)
    b_sp1      = = (S,) vector, lifetime savings (b1, b2, ...bS) with bS=0
    n_errors   = (S,) vector, labor supply Euler errors
    b_errors   = (S-1,) vector, savings Euler errors

    FILES CREATED BY THIS FUNCTION: None

    RETURNS: array of n_errors and b_errors
    --------------------------------------------------------------------
    '''
    (r, w, beta, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff, S) = args
    nvec = guesses[:S]
    bvec1 = guesses[S:]

    b_s = np.append(0.0, bvec1)
    b_sp1 = np.append(bvec1, 0.0)
    cvec = hh.get_cons(r, w, b_s, b_sp1, nvec)
    n_args = (w, sigma, l_tilde, chi_n_vec, b_ellip, upsilon, diff, cvec)
    n_errors = hh.get_n_errors(nvec, *n_args)
    b_args = (r, beta, sigma, diff)
    b_errors = hh.get_b_errors(cvec, *b_args)

    errors = np.append(n_errors, b_errors)

    return errors
예제 #2
0
파일: SS.py 프로젝트: snowdj/WB-India
def SS_EulErrs(bvec, *args):
    '''
    --------------------------------------------------------------------
    --------------------------------------------------------------------
    INPUTS:
    bvec = (S-1,) vector, lifetime savings
    args = length 7 tuple, (nvec, beta, sigma, A, alpha, delta, EulDiff)

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        aggr.get_K()
        aggr.get_L()
        firms.get_r()
        firms.get_w()
        hh.get_cons()
        hh.get_b_errors()

    OBJECTS CREATED WITHIN FUNCTION:
    nvec     = (S,) vector, exogenous lifetime labor supply n_s
    beta     = scalar in (0,1), discount factor for each model per
    sigma    = scalar > 0, coefficient of relative risk aversion
    A        = scalar > 0, total factor productivity parameter in
               firms' production function
    alpha    = scalar in (0,1), capital share of income
    delta    = scalar in [0,1], model-period depreciation rate of
               capital
    EulDiff  = boolean, =True if want difference version of Euler errors
               beta*(1+r)*u'(c2) - u'(c1), =False if want ratio version
               version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1
    K        = scalar > 0, aggregate capital stock
    K_cstr   = boolean, =True if K < epsilon
    L        = scalar > 0, exogenous aggregate labor
    r_params = length 3 tuple, (A, alpha, delta)
    r        = scalar > 0, interest rate
    w_params = length 2 tuple, (A, alpha)
    w        = scalar > 0, wage
    c_args   = length 3 tuple, (nvec, r, w)
    cvec     = (S,) vector, household consumption c_s
    b_args   = length 4 tuple, (beta, sigma, r, EulDiff)
    errors   = (S-1,) vector, savings Euler errors given bvec

    FILES CREATED BY THIS FUNCTION: None

    RETURNS: errors
    --------------------------------------------------------------------
    '''
    nvec, beta, sigma, A, alpha, delta, EulDiff = args
    K, K_cstr = aggr.get_K(bvec)
    L = aggr.get_L(nvec)
    r_params = (A, alpha, delta)
    r = firms.get_r(K, L, r_params)
    w_params = (A, alpha)
    w = firms.get_w(K, L, w_params)
    c_args = (nvec, r, w)
    cvec = hh.get_cons(bvec, 0.0, c_args)
    b_args = (beta, sigma, r, EulDiff)
    errors = hh.get_b_errors(cvec, b_args)

    return errors
예제 #3
0
파일: TPI.py 프로젝트: snowdj/WB-India
def LfEulerSys(bvec, *args):
    '''
    --------------------------------------------------------------------
    Generates vector of all Euler errors for a given bvec, which errors
    characterize all optimal lifetime decisions, where p is an integer
    in [2, S] representing the remaining periods of life
    --------------------------------------------------------------------
    INPUTS:
    bvec       = (p-1,) vector, remaining lifetime savings decisions
                 where p is the number of remaining periods
    args       = length 7 tuple, (beta, sigma, beg_wealth, nvec, rpath,
                 wpath, EulDiff)
    beta       = scalar in [0,1), discount factor
    sigma      = scalar > 0, coefficient of relative risk aversion
    beg_wealth = scalar, wealth at the beginning of first age
    nvec       = (p,) vector, remaining exogenous labor supply
    rpath      = (p,) vector, interest rates over remaining life
    wpath      = (p,) vector, wages rates over remaining life
    EulDiff    = Boolean, =True if want difference version of Euler
                 errors beta*(1+r)*u'(c2) - u'(c1), =False if want ratio
                 version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        c6ssf.get_cvec()
        c6ssf.get_b_errors()

    OBJECTS CREATED WITHIN FUNCTION:
    bvec2        = (p,) vector, remaining savings including initial
                   savings
    cvec         = (p,) vector, remaining lifetime consumption
                   levels implied by bvec2
    c_cnstr      = (p,) Boolean vector, =True if c_{s,t}<=0
    b_err_params = length 2 tuple, (beta, sigma)
    b_err_vec    = (p-1,) vector, Euler errors from lifetime
                   consumption vector

    FILES CREATED BY THIS FUNCTION: None

    RETURNS: b_err_vec
    --------------------------------------------------------------------
    '''
    beta, sigma, beg_wealth, nvec, rpath, wpath, EulDiff = args
    c_args = (nvec, rpath, wpath)
    cvec = hh.get_cons(bvec, beg_wealth, c_args)
    b_args = (beta, sigma, rpath[1:], EulDiff)
    b_err_vec = hh.get_b_errors(cvec, b_args)

    return b_err_vec
예제 #4
0
파일: TPI.py 프로젝트: LeiliPR/OG-JRC
def get_cnbpath(params, rpath, wpath):
    '''
    --------------------------------------------------------------------
    Given time paths for interest rates and wages, this function
    generates matrices for the time path of the distribution of
    individual consumption, labor supply, savings, the corresponding
    Euler errors for the labor supply decision and the savings decision,
    and the residual error of end-of-life savings associated with
    solving each lifetime decision.
    --------------------------------------------------------------------
    INPUTS:
    params  = length 11 tuple, (S, T2, beta, sigma, l_tilde, b_ellip,
              upsilon, chi_n_vec, bvec1, TPI_tol, diff)
    rpath   = (T2+S-1,) vector, equilibrium time path of interest rate
    wpath   = (T2+S-1,) vector, equilibrium time path of the real wage

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        hh.c1_bSp1err()
        hh.get_cnb_vecs()
        hh.get_n_errors()
        hh.get_b_errors()

    OBJECTS CREATED WITHIN FUNCTION:
    S             = integer in [3,80], number of periods an individual
                    lives
    T2            = integer > S, number of periods until steady state
    beta          = scalar in (0,1), discount factor
    sigma         = scalar > 0, coefficient of relative risk aversion
    l_tilde       = scalar > 0, time endowment for each agent each
                    period
    b_ellip       = scalar > 0, fitted value of b for elliptical
                    disutility of labor
    upsilon       = scalar > 1, fitted value of upsilon for elliptical
                    disutility of labor
    chi_n_vec     = (S,) vector, values for chi^n_s
    bvec1         = (S,) vector, initial period savings distribution
    TPI_tol       = scalar > 0, tolerance level for fsolve's in TPI
    diff          = boolean, =True if want difference version of Euler
                    errors beta*(1+r)*u'(c2) - u'(c1), =False if want
                    ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1
    cpath         = (S, T2+S-1) matrix, time path of the distribution of
                    consumption
    npath         = (S, T2+S-1) matrix, time path of the distribution of
                    labor supply
    bpath         = (S, T2+S-1) matrix, time path of the distribution of
                    savings
    n_err_path    = (S, T2+S-1) matrix, time path of distribution of
                    labor supply Euler errors
    b_err_path    = (S, T2+S-1) matrix, time path of distribution of
                    savings Euler errors
    bSp1_err_path = (S, T2) matrix, residual last period savings, which
                    should be close to zero in equilibrium. Nonzero
                    elements of matrix should only be in first column
                    and first row
    c1_options    = length 1 dict, options for
                    opt.root(hh.c1_bSp1err,...)
    b_err_params  = length 2 tuple, args to pass into
                    hh.get_b_errors()
    p             = integer in [1, S-1], index representing number of
                    periods remaining in a lifetime, used to solve
                    incomplete lifetimes
    c1_init       = scalar > 0, guess for initial period consumption
    c1_args       = length 10 tuple, args to pass into
                    opt.root(hh.c1_bSp1err,...)
    results_c1    = results object, solution from
                    opt.root(hh.c1_bSp1err,...)
    c1            = scalar > 0, optimal initial consumption
    cnb_args      = length 8 tuple, args to pass into
                    hh.get_cnb_vecs()
    cvec          = (p,) vector, individual lifetime consumption
                    decisions
    nvec          = (p,) vector, individual lifetime labor supply
                    decisions
    bvec          = (p,) vector, individual lifetime savings decisions
    b_Sp1         = scalar, savings in last period for next period.
                    Should be zero in equilibrium
    DiagMaskc     = (p, p) boolean identity matrix
    DiagMaskb     = (p-1, p-1) boolean identity matrix
    n_err_params  = length 5 tuple, args to pass into hh.get_n_errors()
    n_err_vec     = (p,) vector, individual lifetime labor supply Euler
                    errors
    b_err_vec     = (p-1,) vector, individual lifetime savings Euler
                    errors
    t             = integer in [0,T2-1], index of time period (minus 1)

    FILES CREATED BY THIS FUNCTION: None

    RETURNS: cpath, npath, bpath, n_err_path, b_err_path, bSp1_err_path
    --------------------------------------------------------------------
    '''
    (S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bvec1, TPI_tol,
     diff) = params
    cpath = np.zeros((S, T2 + S - 1))
    npath = np.zeros((S, T2 + S - 1))
    bpath = np.append(bvec1.reshape((S, 1)), np.zeros((S, T2 + S - 2)), axis=1)
    n_err_path = np.zeros((S, T2 + S - 1))
    b_err_path = np.zeros((S, T2 + S - 1))
    bSp1_err_path = np.zeros((S, T2))
    # Solve the incomplete remaining lifetime decisions of agents alive
    # in period t=1 but not born in period t=1
    c1_options = {'maxiter': 500}
    b_err_params = (beta, sigma)
    for p in range(1, S):
        c1_init = 0.1
        c1_args = (bvec1[-p], beta, sigma, l_tilde, b_ellip, upsilon,
                   chi_n_vec[-p:], rpath[:p], wpath[:p], diff)
        results_c1 = \
            opt.root(hh.c1_bSp1err, c1_init, args=(c1_args),
                     method='lm', tol=TPI_tol, options=(c1_options))
        c_1 = results_c1.x
        cnb_args = (bvec1[-p], beta, sigma, l_tilde, b_ellip, upsilon,
                    chi_n_vec[-p:], diff)
        cvec, nvec, bvec, b_Sp1 = \
            hh.get_cnb_vecs(c_1, rpath[:p], wpath[:p], cnb_args)
        DiagMaskc = np.eye(p, dtype=bool)
        DiagMaskb = np.eye(p - 1, dtype=bool)
        cpath[-p:, :p] = DiagMaskc * cvec + cpath[-p:, :p]
        npath[-p:, :p] = DiagMaskc * nvec + npath[-p:, :p]
        n_err_args = (wpath[:p], cvec, sigma, l_tilde, chi_n_vec[-p:], b_ellip,
                      upsilon, diff)
        n_err_vec = hh.get_n_errors(nvec, n_err_args)
        n_err_path[-p:, :p] = (DiagMaskc * n_err_vec + n_err_path[-p:, :p])
        bSp1_err_path[-p, 0] = b_Sp1
        if p > 1:
            bpath[S - p + 1:,
                  1:p] = (DiagMaskb * bvec[1:] + bpath[S - p + 1:, 1:p])
            b_err_vec = hh.get_b_errors(b_err_params, rpath[1:p], cvec, diff)
            b_err_path[S - p + 1:, 1:p] = (DiagMaskb * b_err_vec +
                                           b_err_path[S - p + 1:, 1:p])
            # print('p=', p, ', max. abs. all errs: ',
            #       np.hstack((n_err_vec, b_err_vec, b_Sp1)).max())

    # Solve the remaining lifetime decisions of agents born between
    # period t=1 and t=T (complete lifetimes)
    DiagMaskc = np.eye(S, dtype=bool)
    DiagMaskb = np.eye(S - 1, dtype=bool)
    for t in range(T2):  # Go from periods 1 to T (columns 0 to T-1)
        if t == 0:
            c1_init = 0.1
        else:
            c1_init = cpath[0, t - 1]
        c1_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec,
                   rpath[t:t + S], wpath[t:t + S], diff)
        results_c1 = \
            opt.root(hh.c1_bSp1err, c1_init, args=(c1_args),
                     method='lm', tol=TPI_tol, options=(c1_options))
        c_1 = results_c1.x
        cnb_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec,
                    diff)
        cvec, nvec, bvec, b_Sp1 = \
            hh.get_cnb_vecs(c_1, rpath[t:t + S], wpath[t:t + S],
                            cnb_args)
        cpath[:, t:t + S] = DiagMaskc * cvec + cpath[:, t:t + S]
        npath[:, t:t + S] = DiagMaskc * nvec + npath[:, t:t + S]
        n_err_args = (wpath[t:t + S], cvec, sigma, l_tilde, chi_n_vec, b_ellip,
                      upsilon, diff)
        n_err_vec = hh.get_n_errors(nvec, n_err_args)
        n_err_path[:,
                   t:t + S] = (DiagMaskc * n_err_vec + n_err_path[:, t:t + S])
        bpath[:, t:t + S] = DiagMaskc * bvec + bpath[:, t:t + S]
        b_err_vec = hh.get_b_errors(b_err_params, rpath[t + 1:t + S], cvec,
                                    diff)
        b_err_path[1:, t + 1:t + S] = (DiagMaskb * b_err_vec +
                                       b_err_path[1:, t + 1:t + S])
        bSp1_err_path[0, t] = b_Sp1
        # print('t=', t, ', max. abs. all errs: ',
        #       np.absolute(np.hstack((n_err_vec, b_err_vec,
        #                              b_Sp1))).max())

    return cpath, npath, bpath, n_err_path, b_err_path, bSp1_err_path
예제 #5
0
파일: TPI.py 프로젝트: LeiliPR/OG-JRC
def twist_doughnut(guesses, td_args):
    '''
    Parameters:
        guesses = distribution of capital and labor (various length list)
        w   = wage rate ((T+S)x1 array)
        r   = rental rate ((T+S)x1 array)
        BQ = aggregate bequests ((T+S)x1 array)
        T_H = 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)
    '''
    rpath, wpath, s, t, S, T2, beta, sigma, l_tilde, b_ellip, upsilon, \
                chi_n_vec, initial_b, diff = td_args

    length = len(guesses) // 2
    b_guess = np.array(guesses[:length])
    n_guess = np.array(guesses[length:])

    b_guess[-1] = 0.0  # save nothing in last period

    if length == S:
        b_s = np.array([0] + list(b_guess[:-1]))
    else:
        # b_s = np.array([(initial_b[-(s + 3)])] + list(b_guess[:-1]))
        b_s = np.array([(initial_b[-(s + 2)])] + list(b_guess[:-1]))

    w = wpath[t:t + length]
    r = rpath[t:t + length]

    # Euler equations
    cons = hh.get_cons(r, w, b_s, b_guess, n_guess)

    b_params = (beta, sigma)
    euler_errors = hh.get_b_errors(b_params, r[1:], cons, diff)
    error1 = np.append(euler_errors, 0.0)

    n_args = (w, cons, sigma, l_tilde, chi_n_vec[-length:], b_ellip, upsilon,
              diff)
    error2 = hh.get_n_errors(n_guess, n_args)

    # Check and punish constraint violations
    mask1 = n_guess < 0
    error2[mask1] += 1e14
    mask2 = n_guess > l_tilde
    error2[mask2] += 1e14
    mask3 = cons < 0
    error2[mask3] += 1e14

    return list(error1.flatten()) + list(error2.flatten())
예제 #6
0
def inner_loop(rpath, wpath, args):
    '''
    --------------------------------------------------------------------
    Given time paths for interest rates and wages, this function
    generates matrices for the time path of the distribution of
    individual consumption, labor supply, savings, the corresponding
    Euler errors for the labor supply decision and the savings decision,
    and the residual error of end-of-life savings associated with
    solving each lifetime decision.
    --------------------------------------------------------------------
    INPUTS:
    rpath = (T2+S-1,) vector, equilibrium time path of interest rate
    wpath = (T2+S-1,) vector, equilibrium time path of the real wage
    args  = length 12 tuple, (S, T2, beta, sigma, l_tilde, b_ellip,
            upsilon, chi_n_vec, bvec1, n_ss, In_Tol, diff)
    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        hh.get_n_errors()
        hh.get_b_errors()
    OBJECTS CREATED WITHIN FUNCTION:
    S             = integer in [3,80], number of periods an individual
                    lives
    T2            = integer > S, number of periods until steady state
    beta          = scalar in (0,1), discount factor
    sigma         = scalar > 0, coefficient of relative risk aversion
    l_tilde       = scalar > 0, time endowment for each agent each
                    period
    b_ellip       = scalar > 0, fitted value of b for elliptical
                    disutility of labor
    upsilon       = scalar > 1, fitted value of upsilon for elliptical
                    disutility of labor
    chi_n_vec     = (S,) vector, values for chi^n_s
    bvec1         = (S,) vector, initial period savings distribution
    In _tol       = scalar > 0, tolerance level for fsolve's in TPI
    diff          = boolean, =True if want difference version of Euler
                    errors beta*(1+r)*u'(c2) - u'(c1), =False if want
                    ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1
    cpath         = (S, T2+S-1) matrix, time path of the distribution of
                    consumption
    npath         = (S, T2+S-1) matrix, time path of the distribution of
                    labor supply
    bpath         = (S, T2+S-1) matrix, time path of the distribution of
                    savings
    n_err_path    = (S, T2+S-1) matrix, time path of distribution of
                    labor supply Euler errors
    b_err_path    = (S, T2+S-1) matrix, time path of distribution of
                    savings Euler errors
    bSp1_err_path = (S, T2) matrix, residual last period savings, which
                    should be close to zero in equilibrium. Nonzero
                    elements of matrix should only be in first column
                    and first row
    b_err_params  = length 2 tuple, args to pass into
                    hh.get_b_errors()
    p             = integer in [1, S-1], index representing number of
                    periods remaining in a lifetime, used to solve
                    incomplete lifetimes
    cvec          = (p,) vector, individual lifetime consumption
                    decisions
    nvec          = (p,) vector, individual lifetime labor supply
                    decisions
    bvec          = (p,) vector, individual lifetime savings decisions
    b_Sp1         = scalar, savings in last period for next period.
                    Should be zero in equilibrium
    DiagMaskc     = (p, p) boolean identity matrix
    DiagMaskb     = (p-1, p-1) boolean identity matrix
    n_err_params  = length 5 tuple, args to pass into hh.get_n_errors()
    n_err_vec     = (p,) vector, individual lifetime labor supply Euler
                    errors
    b_err_vec     = (p-1,) vector, individual lifetime savings Euler
                    errors
    t             = integer in [0,T2-1], index of time period (minus 1)
    FILES CREATED BY THIS FUNCTION: None
    RETURNS: cpath, npath, bpath, n_err_path, b_err_path, bSp1_err_path
    --------------------------------------------------------------------
    '''
    (S, T2, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, bvec1, n_ss,
     In_Tol, diff) = args
    cpath = np.zeros((S, T2 + S - 1))
    npath = np.zeros((S, T2 + S - 1))
    bpath = np.append(bvec1.reshape((S, 1)), np.zeros((S, T2 + S - 2)), axis=1)
    n_err_path = np.zeros((S, T2 + S - 1))
    b_err_path = np.zeros((S, T2 + S - 1))
    b_err_args = (beta, sigma, diff)

    # Solve the incomplete remaining lifetime decisions of agents alive
    # in period t=1 but not born in period t=1
    for p in range(1, S):

        if p == 1:
            # p=1 individual only has an s=S labor supply decision n_S
            n_S1_init = n_ss[-1]
            nS1_args = (wpath[0], sigma, l_tilde, chi_n_vec[-1], b_ellip,
                        upsilon, diff, rpath[0], bvec1[-1], 0.0)
            results_nS1 = opt.root(hh.get_n_errors,
                                   n_S1_init,
                                   args=(nS1_args),
                                   method='lm',
                                   tol=In_Tol)
            n_S1 = results_nS1.x
            npath[-1, 0] = n_S1
            n_err_path[-1, 0] = results_nS1.fun
            cpath[-1, 0] = hh.get_cons(rpath[0], wpath[0], bvec1[-1], 0.0,
                                       n_S1)

        else:
            # 1<p<S chooses b_{s+1} and n_s and has incomplete lives
            DiagMaskb = np.eye(p - 1, dtype=bool)
            DiagMaskn = np.eye(p, dtype=bool)
            b_sp1_init = np.diag(bpath[S - p + 1:, :p - 1])
            n_s_init = np.hstack(
                (n_ss[S - p], np.diag(npath[S - p + 1:, :p - 1])))
            bn_init = np.hstack((b_sp1_init, n_s_init))
            bn_args = (rpath[:p], wpath[:p], bvec1[-p], p, beta, sigma,
                       l_tilde, chi_n_vec[-p:], b_ellip, upsilon, diff)
            results_bn = opt.root(hh.bn_errors,
                                  bn_init,
                                  args=(bn_args),
                                  tol=In_Tol)
            bvec = results_bn.x[:p - 1]
            nvec = results_bn.x[p - 1:]
            b_s_vec = np.append(bvec1[-p], bvec)
            b_sp1_vec = np.append(bvec, 0.0)
            cvec = hh.get_cons(rpath[:p], wpath[:p], b_s_vec, b_sp1_vec, nvec)
            npath[S - p:, :p] = DiagMaskn * nvec + npath[S - p:, :p]
            bpath[S - p + 1:,
                  1:p] = (DiagMaskb * bvec + bpath[S - p + 1:, 1:p])
            cpath[S - p:, :p] = DiagMaskn * cvec + cpath[S - p:, :p]
            n_args = (wpath[:p], sigma, l_tilde, chi_n_vec[-p:], b_ellip,
                      upsilon, diff, cvec)
            n_errors = hh.get_n_errors(nvec, *n_args)
            n_err_path[S - p:, :p] = (DiagMaskn * n_errors +
                                      n_err_path[S - p:, :p])
            b_errors = hh.get_b_errors(cvec, rpath[1:p], *b_err_args)
            b_err_path[S - p + 1:, 1:p] = (DiagMaskb * b_errors +
                                           b_err_path[S - p + 1:, 1:p])

    # Solve the complete remaining lifetime decisions of agents born
    # between period t=1 and t=T2
    for t in range(T2):
        DiagMaskb = np.eye(S - 1, dtype=bool)
        DiagMaskn = np.eye(S, dtype=bool)
        b_sp1_init = np.diag(bpath[1:, t:t + S - 1])
        if t == 0:
            n_s_init = np.hstack((n_ss[0], np.diag(npath[1:, t:t + S - 1])))
        else:
            n_s_init = np.diag(npath[:, t - 1:t + S - 1])
        bn_init = np.hstack((b_sp1_init, n_s_init))
        bn_args = (rpath[t:t + S], wpath[t:t + S], 0.0, S, beta, sigma,
                   l_tilde, chi_n_vec, b_ellip, upsilon, diff)
        results_bn = opt.root(hh.bn_errors,
                              bn_init,
                              args=(bn_args),
                              tol=In_Tol)
        bvec = results_bn.x[:S - 1]
        nvec = results_bn.x[S - 1:]
        b_s_vec = np.append(0.0, bvec)
        b_sp1_vec = np.append(bvec, 0.0)
        cvec = hh.get_cons(rpath[t:t + S], wpath[t:t + S], b_s_vec, b_sp1_vec,
                           nvec)
        npath[:, t:t + S] = DiagMaskn * nvec + npath[:, t:t + S]
        bpath[1:, t + 1:t + S] = (DiagMaskb * bvec + bpath[1:, t + 1:t + S])
        cpath[:, t:t + S] = DiagMaskn * cvec + cpath[:, t:t + S]
        n_args = (wpath[t:t + S], sigma, l_tilde, chi_n_vec, b_ellip, upsilon,
                  diff, cvec)
        n_errors = hh.get_n_errors(nvec, *n_args)
        n_err_path[:,
                   t:t + S] = (DiagMaskn * n_errors + n_err_path[:, t:t + S])
        b_errors = hh.get_b_errors(cvec, rpath[t + 1:t + S], *b_err_args)
        b_err_path[1:, t + 1:t + S] = (DiagMaskb * b_errors +
                                       b_err_path[1:, t + 1:t + S])

    return cpath, npath, bpath, n_err_path, b_err_path
예제 #7
0
파일: SS.py 프로젝트: rickecon/OG-JRC
def get_SS_bsct(init_vals, args, graphs=False):
    '''
    --------------------------------------------------------------------
    Solve for the steady-state solution of the S-period-lived agent OG
    model with endogenous labor supply using the bisection method for
    the outer loop
    --------------------------------------------------------------------
    INPUTS:
    init_vals = length 5 tuple,
                (Kss_init, Lss_init, rss_init, wss_init,c1_init)
    args      = length 16 tuple, (S, beta, sigma, l_tilde, b_ellip,
                upsilon, chi_n_vec, A, alpha, delta, tax_params,
                fiscal_params, SS_tol, EulDiff, hh_fsolve, KL_outer)
    graphs    = boolean, =True if output steady-state graphs

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        firms.get_r()
        firms.get_w()
        hh.bn_solve()
        hh.c1_bSp1err()
        hh.get_cnb_vecs()
        aggr.get_K()
        aggr.get_L()
        aggr.get_Y()
        aggr.get_C()
        hh.get_cons()
        hh.get_n_errors()
        hh.get_b_errors()
        utils.print_time()

    OBJECTS CREATED WITHIN FUNCTION:
    start_time       = scalar > 0, clock time at beginning of program
    Kss_init         = scalar > 0, initial guess for steady-state aggregate
                       capital stock
    Lss_init         = scalar > 0, initial guess for steady-state aggregate
                       labor
    rss_init         = scalar > 0, initial guess for steady-state interest
                       rate
    wss_init         = scalar > 0, initial guess for steady-state wage
    c1_init          = scalar > 0, initial guess for first period consumpt'n
    S                = integer in [3, 80], number of periods an individual
                       lives
    beta             = scalar in (0,1), discount factor for each model per
    sigma            = scalar > 0, coefficient of relative risk aversion
    l_tilde          = scalar > 0, time endowment for each agent each period
    b_ellip          = scalar > 0, fitted value of b for elliptical
                       disutility of labor
    upsilon          = scalar > 1, fitted value of upsilon for elliptical
                       disutility of labor
    chi_n_vec        = (S,) vector, values for chi^n_s
    A                = scalar > 0, total factor productivity parameter in
                       firms' production function
    alpha            = scalar in (0,1), capital share of income
    delta            = scalar in [0,1], model-period depreciation rate of
                       capital
    tax_params       = length 3 tuple, (tau_l, tau_k, tau_c)
    fiscal_params    = length 7 tuple, (tG1, tG2, alpha_X, alpha_G, rho_G,
                                     alpha_D, alpha_D0)
    SS_tol           = scalar > 0, tolerance level for steady-state fsolve
    EulDiff          = Boolean, =True if want difference version of Euler
                       errors beta*(1+r)*u'(c2) - u'(c1), =False if want
                       ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1
    hh_fsolve        = Boolean, =True if want to solve HH problem with one
                       large root finder call
    tau_l            = scalar, marginal tax rate on labor income
    tau_k            = scalar, marginal tax rate on capital income
    tau_c            = scalar, marginal tax rate on corporate income
    tG1              = integer, model period when budget closure rule begins
    tG2              = integer, model period when budget is closed
    alpha_X          = scalar, ratio of lump sum transfers to GDP
    alpha_G          = scalar, ratio of government spending to GDP prior to
                            budget closure rule beginning
    rho_G            = scalar in (0,1), rate of convergence to SS budget
    alpha_D          = scalar, steady-state debt to GDP ratio
    alpha_D0         = scalar, debt to GDP ratio in the initial period
    maxiter_SS       = integer >= 1, maximum number of iterations in outer
                       loop bisection method
    iter_SS          = integer >= 0, index of iteration number
    mindist_SS       = scalar > 0, minimum distance tolerance for
                       convergence
    dist_SS          = scalar > 0, distance metric for current iteration
    xi_SS            = scalar in (0,1], updating parameter
    KL_init          = (2,) vector, (K_init, L_init)
    c1_options       = length 1 dict, options to pass into
                       opt.root(c1_bSp1err,...)
    cnb_args         = length 9 tuple, args to pass into get_cnb_vecs()
    r_params         = length 3 tuple, args to pass into get_r()
    w_params         = length 2 tuple, args to pass into get_w()
    Y_params         = length 2 tuple, args to pass into get_Y()
    K_init           = scalar, initial value of aggregate capital stock
    L_init           = scalar, initial value of aggregate labor
    r_init           = scalar, initial value for interest rate
    w_init           = scalar, initial value for wage
    Y_init           = scalar, initial value for output
    x_init           = scalar, initial value for per household lump sum transfers
    rpath            = (S,) vector, lifetime path of interest rates
    wpath            = (S,) vector, lifetime path of wages
    xpath            = (S,) vector, lifetime path of lump sum transfers
    c1_args          = length 10 tuple, args to pass into c1_bSp1err()
    results_c1       = results object, root finder results from
                       opt.root(c1_bSp1err,...)
    c1_new           = scalar, updated value of optimal c1 given r_init and
                       w_init
    cvec_new         = (S,) vector, updated values for lifetime consumption
    nvec_new         = (S,) vector, updated values for lifetime labor supply
    b_splus1_vec_new = (S,) vector, updated values for lifetime savings
                       (b1, b2,...bS)
    b_s_vec_new      = (S,) vector, updated values for lifetime savings enter
                       period with (b0, b1,...bS)
    b_Sp1_new        = scalar, updated value for savings in last period,
                       should be arbitrarily close to zero
    B_new            = scalar, aggregate household savings given bvec_new
    B_cnstr          = boolean, =True if K_new <= 0
    L_new            = scalar, updated L given nvec_new
    debt_ss          = scalar, government debt in the SS
    K_new            = scalar, updated K given bvec_new and SS debt
    K_cnstr          = boolean, =True if K_new <= 0
    KL_new           = (2,) vector, updated K and L given bvec_new, nvec_new
    K_ss             = scalar > 0, steady-state aggregate capital stock
    L_ss             = scalar > 0, steady-state aggregate labor
    B_ss             = scalar > 0, steady-state aggregate savings
    r_ss             = scalar > 0, steady-state interest rate
    w_ss             = scalar > 0, steady-state wage
    x_ss             = scalar > 0, steady-state per household lump sum transfers
    c1_ss            = scalar > 0, steady-state consumption in first period
    c_ss             = (S,) vector, steady-state lifetime consumption
    n_ss             = (S,) vector, steady-state lifetime labor supply
    b_splus1_ss      = (S,) vector, steady-state lifetime savings
                       (b1_ss, b2_ss, ...bS+1_ss)
    b_s_ss           = (S,) vector, steady-state lifetime savings enter period with
                       (b0_ss, b2_ss, ...bS_ss) where b0_ss=0
    b_Sp1_ss         = scalar, steady-state savings for period after last
                       period of life. b_Sp1_ss approx. 0 in equilibrium
    Y_ss             = scalar > 0, steady-state aggregate output (GDP)
    C_ss             = scalar > 0, steady-state aggregate consumption
    n_err_params     = length 5 tuple, args to pass into get_n_errors()
    n_err_ss         = (S,) vector, lifetime labor supply Euler errors
    b_err_params     = length 2 tuple, args to pass into get_b_errors()
    b_err_ss         = (S-1) vector, lifetime savings Euler errors
    rev_params       = length 4 tuple, (A, alpha, delta, tax_params)
    R_ss             = scalar, steady-state tax revenue
    X_ss             = scalar, total steady-state government transfers
    debt_service_ss  = scalar, steady-state debt service cost
    G_ss             = steady-state government spending
    RCerr_ss         = scalar, resource constraint error
    ss_time          = scalar, seconds elapsed to run steady-state comput'n
    ss_output        = length 17 dict, steady-state objects {n_ss, b_s_ss,
                       b_splus1_ss, c_ss, b_Sp1_ss, w_ss, r_ss, B_ss, K_ss,
                       L_ss, Y_ss, C_ss, G_ss, n_err_ss, b_err_ss, RCerr_ss,
                       ss_time}


    FILES CREATED BY THIS FUNCTION:
        SS_bc.png
        SS_n.png

    RETURNS: ss_output
    --------------------------------------------------------------------
    '''
    start_time = time.clock()
    Kss_init, Lss_init, rss_init, wss_init, c1_init = init_vals
    (S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta,
     tax_params, fiscal_params, SS_tol, EulDiff, hh_fsolve) = args
    tau_l, tau_k, tau_c = tax_params
    tG1, tG2, alpha_X, alpha_G, rho_G, alpha_D, alpha_D0 = fiscal_params
    maxiter_SS = 200
    iter_SS = 0
    mindist_SS = 1e-10
    dist_SS = 10
    xi_SS = 0.15
    KL_init = np.array([Kss_init, Lss_init])
    r_params = (A, alpha, delta, tau_c)
    w_params = (A, alpha)
    Y_params = (A, alpha)
    inner_loop_params = (c1_init, S, beta, sigma, l_tilde, \
                         b_ellip, upsilon, chi_n_vec, A, alpha, delta,\
                         tax_params, fiscal_params,\
                         EulDiff, hh_fsolve, SS_tol)
    while (iter_SS < maxiter_SS) and (dist_SS >= mindist_SS):
        iter_SS += 1
        K_init, L_init = KL_init
        r_init = firms.get_r(r_params, K_init, L_init)
        w_init = firms.get_w(w_params, K_init, L_init)
        Y_init = aggr.get_Y(Y_params, K_init, L_init)
        x_init = (alpha_X * Y_init) / S

        B_new, K_new, L_new, Y_new, debt, cvec, nvec, b_s_vec, b_splus1_vec,\
            b_Sp1, x_new, r_new, w_new\
             =  inner_loop(r_init, w_init, Y_init, x_init, inner_loop_params)

        KL_new = np.array([K_new, L_new])
        dist_SS = ((KL_new - KL_init)**2).sum()
        KL_init = xi_SS * KL_new + (1 - xi_SS) * KL_init

        rev_params = (A, alpha, delta, tax_params)
        R_new = tax.revenue(r_new, w_new, b_s_vec, nvec, K_new, L_new,
                            rev_params)
        X_new = x_new * S
        G_new = R_new - (X_new + debt * r_new)
        print('tax rev to GDP: ', R_new / Y_new)
        print('SS outlays to GDP: ', ((debt * r_new) + X_new + G_new) / Y_new)
        print('SS G spending to GDP: ', G_new / Y_new)
        print('factor prices: ', r_new, w_new)
        print('SS Iteration=', iter_SS, ', SS Distance=', dist_SS)

    B_ss, K_ss, L_ss, Y_ss, debt_ss, c_ss, n_ss, b_s_ss, b_splus1_ss,\
        b_Sp1_ss, x_ss, r_ss, w_ss\
         =  inner_loop(r_new, w_new, Y_new, x_new, inner_loop_params)

    C_ss = aggr.get_C(c_ss)
    n_err_args = (w_ss, c_ss, sigma, l_tilde, chi_n_vec, b_ellip, upsilon,
                  tau_l, EulDiff)
    n_err_ss = hh.get_n_errors(n_ss, n_err_args)
    b_err_params = (beta, sigma, tau_k)
    b_err_ss = hh.get_b_errors(b_err_params, r_ss, c_ss, EulDiff)
    rev_params = (A, alpha, delta, tax_params)
    R_ss = tax.revenue(r_ss, w_ss, b_s_ss, n_ss, K_ss, L_ss, rev_params)
    X_ss = x_ss * S
    debt_service_ss = r_ss * alpha_D * Y_ss
    G_ss = R_ss - (X_ss + debt_service_ss)
    RCerr_ss = Y_ss - C_ss - delta * K_ss - G_ss

    print('SS tax rev to GDP: ', R_ss / Y_ss)
    print('SS outlays to GDP: ', (debt_service_ss + X_ss + G_ss) / Y_ss)
    print('SS G spending to GDP: ', G_ss / Y_ss)

    ss_time = time.clock() - start_time

    ss_output = {
        'n_ss': n_ss,
        'b_s_ss': b_s_ss,
        'b_splus1_ss': b_splus1_ss,
        'c_ss': c_ss,
        'b_Sp1_ss': b_Sp1_ss,
        'w_ss': w_ss,
        'r_ss': r_ss,
        'B_ss': B_ss,
        'K_ss': K_ss,
        'L_ss': L_ss,
        'Y_ss': Y_ss,
        'C_ss': C_ss,
        'G_ss': G_ss,
        'X_ss': X_ss,
        'n_err_ss': n_err_ss,
        'b_err_ss': b_err_ss,
        'RCerr_ss': RCerr_ss,
        'ss_time': ss_time
    }
    print('n_ss is: ', n_ss)
    print('b_s_ss is: ', b_s_ss)
    print('K_ss=', K_ss, ', L_ss=', L_ss)
    print('r_ss=', r_ss, ', w_ss=', w_ss)
    print('Maximum abs. labor supply Euler error is: ',
          np.absolute(n_err_ss).max())
    print('Maximum abs. savings Euler error is: ', np.absolute(b_err_ss).max())
    print('Resource constraint error is: ', RCerr_ss)
    print('Steay-state government spending is: ', G_ss)
    if G_ss < 0:
        print('WARNING: SS debt to GDP ratio and tax policy are generating ' +
              'negative government spending.')
    print('Steay-state tax revenue, transfers, and debt service are: ', R_ss,
          X_ss, debt_service_ss)
    print('Steady-state residual savings b_Sp1 is: ', b_Sp1_ss)

    # Print SS computation time
    utils.print_time(ss_time, 'SS')

    if graphs:
        '''
        ----------------------------------------------------------------
        cur_path    = string, path name of current directory
        output_fldr = string, folder in current path to save files
        output_dir  = string, total path of images folder
        output_path = string, path of file name of figure to be saved
        age_pers    = (S,) vector, ages from 1 to S
        ----------------------------------------------------------------
        '''
        # Create directory if images directory does not already exist
        cur_path = os.path.split(os.path.abspath(__file__))[0]
        output_fldr = 'images'
        output_dir = os.path.join(cur_path, output_fldr)
        if not os.access(output_dir, os.F_OK):
            os.makedirs(output_dir)

        # Plot steady-state consumption and savings distributions
        age_pers = np.arange(1, S + 1)
        fig, ax = plt.subplots()
        plt.plot(age_pers, c_ss, marker='D', label='Consumption')
        plt.plot(age_pers, b_splus1_ss, marker='D', label='Savings')
        # for the minor ticks, use no labels; default NullFormatter
        minorLocator = MultipleLocator(1)
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        # plt.title('Steady-state consumption and savings', fontsize=20)
        plt.xlabel(r'Age $s$')
        plt.ylabel(r'Units of consumption')
        plt.xlim((0, S + 1))
        # plt.ylim((-1.0, 1.15 * (b_ss.max())))
        plt.legend(loc='upper left')
        output_path = os.path.join(output_dir, 'SS_bc')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

        # Plot steady-state labor supply distributions
        fig, ax = plt.subplots()
        plt.plot(age_pers, n_ss, marker='D', label='Labor supply')
        # for the minor ticks, use no labels; default NullFormatter
        minorLocator = MultipleLocator(1)
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        # plt.title('Steady-state labor supply', fontsize=20)
        plt.xlabel(r'Age $s$')
        plt.ylabel(r'Labor supply')
        plt.xlim((0, S + 1))
        # plt.ylim((-0.1, 1.15 * (n_ss.max())))
        plt.legend(loc='upper right')
        output_path = os.path.join(output_dir, 'SS_n')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

    return ss_output
예제 #8
0
파일: SS.py 프로젝트: rickecon/OG-JRC
def get_SS(init_vals, args, graphs=False):
    '''
    --------------------------------------------------------------------
    Solve for the steady-state solution of the S-period-lived agent OG
    model with endogenous labor supply and a small open economy.
    --------------------------------------------------------------------
    INPUTS:
    init_vals = length 5 tuple,
                (Kss_init, Lss_init, rss_init, wss_init,c1_init)
    args      = length 14 tuple, (S, beta, sigma, l_tilde, b_ellip,
                upsilon, chi_n_vec, A, alpha, delta, SS_tol, EulDiff,
                hh_fsolve, KL_outer)
    graphs    = boolean, =True if output steady-state graphs

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        firms.get_r()
        firms.get_w()
        hh.bn_solve()
        hh.c1_bSp1err()
        hh.get_cnb_vecs()
        aggr.get_K()
        aggr.get_L()
        aggr.get_Y()
        aggr.get_C()
        hh.get_cons()
        hh.get_n_errors()
        hh.get_b_errors()
        utils.print_time()

    OBJECTS CREATED WITHIN FUNCTION:
    start_time   = scalar > 0, clock time at beginning of program
    Kss_init     = scalar > 0, initial guess for steady-state aggregate
                   capital stock supplied
    Lss_init     = scalar > 0, initial guess for steady-state aggregate
                   labor
    rss_init     = scalar > 0, initial guess for steady-state interest
                   rate
    wss_init     = scalar > 0, initial guess for steady-state wage
    c1_init      = scalar > 0, initial guess for first period consumpt'n
    S            = integer in [3, 80], number of periods an individual
                   lives
    beta         = scalar in (0,1), discount factor for each model per
    sigma        = scalar > 0, coefficient of relative risk aversion
    l_tilde      = scalar > 0, time endowment for each agent each period
    b_ellip      = scalar > 0, fitted value of b for elliptical
                   disutility of labor
    upsilon      = scalar > 1, fitted value of upsilon for elliptical
                   disutility of labor
    chi_n_vec    = (S,) vector, values for chi^n_s
    A            = scalar > 0, total factor productivity parameter in
                   firms' production function
    alpha        = scalar in (0,1), capital share of income
    delta        = scalar in [0,1], model-period depreciation rate of
                   capital
    SS_tol       = scalar > 0, tolerance level for steady-state fsolve
    EulDiff      = Boolean, =True if want difference version of Euler
                   errors beta*(1+r)*u'(c2) - u'(c1), =False if want
                   ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1
    maxiter_SS   = integer >= 1, maximum number of iterations in outer
                   loop bisection method
    iter_SS      = integer >= 0, index of iteration number
    mindist_SS   = scalar > 0, minimum distance tolerance for
                   convergence
    dist_SS      = scalar > 0, distance metric for current iteration
    xi_SS        = scalar in (0,1], updating parameter
    KL_init      = (2,) vector, (K_init, L_init)
    c1_options   = length 1 dict, options to pass into
                   opt.root(c1_bSp1err,...)
    cnb_args     = length 8 tuple, args to pass into get_cnb_vecs()
    #r_params     = length 3 tuple, args to pass into get_r()
    w_params     = length 2 tuple, args to pass into get_w()
    K_init       = scalar, initial value of aggregate capital stock supplied
    L_init       = scalar, initial value of aggregate labor
    r_init       = scalar, initial value for interest rate
    w_init       = scalar, initial value for wage
    K_d          = scalar, capital demand
    rpath        = (S,) vector, lifetime path of interest rates
    wpath        = (S,) vector, lifetime path of wages
    c1_args      = length 10 tuple, args to pass into c1_bSp1err()
    results_c1   = results object, root finder results from
                   opt.root(c1_bSp1err,...)
    c1_new       = scalar, updated value of optimal c1 given r_init and
                   w_init
    cvec_new     = (S,) vector, updated values for lifetime consumption
    nvec_new     = (S,) vector, updated values for lifetime labor supply
    b_s_new      = (S,) vector, updated values for lifetime wealth
    b_splus1_new = (S,) vector, updated values for lifetime savings
                   (b1, b2,...bS)
    b_Sp1_new    = scalar, updated value for savings in last period,
                   should be arbitrarily close to zero
    K_new        = scalar, updated K given bvec_new
    K_cnstr      = boolean, =True if K_new <= 0
    L_new        = scalar, updated L given nvec_new
    KL_new       = (2,) vector, updated K and L given bvec_new, nvec_new
    K_ss         = scalar > 0, steady-state aggregate capital stock supplied
    K_d_ss       = scalar > 0, steady-state aggregate capital stock demanded
    L_ss         = scalar > 0, steady-state aggregate labor
    r_ss         = scalar > 0, steady-state interest rate
    w_ss         = scalar > 0, steady-state wage
    c1_ss        = scalar > 0, steady-state consumption in first period
    c_ss         = (S,) vector, steady-state lifetime consumption
    n_ss         = (S,) vector, steady-state lifetime labor supply
    b_s_ss       = (S,) vector, steady-state wealth enter period with
    b_splus1_ss  = (S,) vector, steady-state lifetime savings
                   (b1_ss, b2_ss, ...bS_ss) where b1_ss=0
    b_Sp1_ss     = scalar, steady-state savings for period after last
                   period of life. b_Sp1_ss approx. 0 in equilibrium
    Y_params     = length 2 tuple, (A, alpha)
    Y_ss         = scalar > 0, steady-state aggregate output (GDP)
    C_ss         = scalar > 0, steady-state aggregate consumption
    n_err_params = length 5 tuple, args to pass into get_n_errors()
    n_err_ss     = (S,) vector, lifetime labor supply Euler errors
    b_err_params = length 2 tuple, args to pass into get_b_errors()
    b_err_ss     = (S-1) vector, lifetime savings Euler errors
    RCerr_ss     = scalar, resource constraint error
    ss_time      = scalar, seconds elapsed to run steady-state comput'n
    ss_output    = length 14 dict, steady-state objects {n_ss, b_ss,
                   c_ss, b_Sp1_ss, w_ss, r_ss, K_ss, L_ss, Y_ss, C_ss,
                   n_err_ss, b_err_ss, RCerr_ss, ss_time}

    FILES CREATED BY THIS FUNCTION:
        SS_bc.png
        SS_n.png

    RETURNS: ss_output
    --------------------------------------------------------------------
    '''
    start_time = time.clock()
    c1_init = init_vals
    (S, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta,
     r_star, SS_tol, EulDiff, hh_fsolve) = args
    c1_options = {'maxiter': 500}
    cnb_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec,
                EulDiff)
    w_params = (A, alpha, delta)
    K_params = (A, alpha, delta)
    r_ss = r_star
    w_ss = firms.get_w(r_ss, w_params)

    if hh_fsolve:
        b_init = np.ones((S - 1, 1)) * 0.05
        n_init = np.ones((S, 1)) * 0.4
        guesses = np.append(b_init, n_init)
        bn_params = (r_ss, w_ss, S, beta, sigma, l_tilde, b_ellip, upsilon,
                     chi_n_vec, EulDiff)
        [solutions, infodict, ier, message] = \
            opt.fsolve(hh.bn_solve, guesses, args=bn_params,
                       xtol=SS_tol, full_output=True)
        euler_errors = infodict['fvec']
        print('Max Euler errors: ', np.absolute(euler_errors).max())
        b_splus1_ss = np.append(solutions[:S - 1], 0.0)
        n_ss = solutions[S - 1:]
        b_Sp1_ss = 0.0
        b_s_ss = np.append(0.0, b_splus1_ss[:-1])
        c_ss = hh.get_cons(r_ss, w_ss, b_s_ss, b_splus1_ss, n_ss)
    else:
        rpath = r_ss * np.ones(S)
        wpath = w_ss * np.ones(S)
        c1_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec,
                   rpath, wpath, EulDiff)
        c1_options = {'maxiter': 500}
        results_c1 = \
            opt.root(hh.c1_bSp1err, c1_new, args=(c1_args),
                     method='lm', tol=SS_tol, options=(c1_options))
        c1_ss = results_c1.x
        cnb_args = (0.0, beta, sigma, l_tilde, b_ellip, upsilon, chi_n_vec,
                    EulDiff)
        c_ss, n_ss, b_s_ss, b_Sp1_ss = hh.get_cnb_vecs(c1_ss, rpath, wpath,
                                                       cnb_args)
        b_splus1_ss = np.append(b_s_ss[1:], b_Sp1_ss)

    L_ss = aggr.get_L_s(n_ss)
    K_s_ss, K_cnstr = aggr.get_K_s(b_s_ss)
    K_d_ss = firms.get_K_d(r_ss, L_ss, K_params)
    Y_params = (A, alpha)
    Y_ss = aggr.get_Y(Y_params, K_d_ss, L_ss)
    C_ss = aggr.get_C(c_ss)
    n_err_args = (w_ss, c_ss, sigma, l_tilde, chi_n_vec, b_ellip, upsilon,
                  EulDiff)
    n_err_ss = hh.get_n_errors(n_ss, n_err_args)
    b_err_params = (beta, sigma)
    b_err_ss = hh.get_b_errors(b_err_params, r_ss, c_ss, EulDiff)
    NX_ss = Y_ss - C_ss - delta * K_s_ss
    RCerr_ss = Y_ss - C_ss - delta * K_s_ss - NX_ss

    ss_time = time.clock() - start_time

    ss_output = {
        'n_ss': n_ss,
        'b_s_ss': b_s_ss,
        'b_splus1_ss': b_splus1_ss,
        'c_ss': c_ss,
        'b_Sp1_ss': b_Sp1_ss,
        'w_ss': w_ss,
        'r_ss': r_ss,
        'K_s_ss': K_s_ss,
        'K_d_ss': K_d_ss,
        'L_ss': L_ss,
        'Y_ss': Y_ss,
        'C_ss': C_ss,
        'n_err_ss': n_err_ss,
        'b_err_ss': b_err_ss,
        'RCerr_ss': RCerr_ss,
        'ss_time': ss_time
    }
    print('n_ss is: ', n_ss)
    print('b_splus1_ss is: ', b_splus1_ss)
    print('K_s_ss=', K_s_ss, 'K_d_ss=', K_d_ss, ', L_ss=', L_ss)
    print('r_ss=', r_ss, ', w_ss=', w_ss)
    print('Maximum abs. labor supply Euler error is: ',
          np.absolute(n_err_ss).max())
    print('Maximum abs. savings Euler error is: ', np.absolute(b_err_ss).max())
    print('Resource constraint error is: ', RCerr_ss)
    print('Net Exports = ', NX_ss)
    print('Output and consumption: ', Y_ss, C_ss)
    print('Steady-state residual savings b_Sp1 is: ', b_Sp1_ss)

    # Print SS computation time
    utils.print_time(ss_time, 'SS')

    if graphs:
        '''
        ----------------------------------------------------------------
        cur_path    = string, path name of current directory
        output_fldr = string, folder in current path to save files
        output_dir  = string, total path of images folder
        output_path = string, path of file name of figure to be saved
        age_pers    = (S,) vector, ages from 1 to S
        ----------------------------------------------------------------
        '''
        # Create directory if images directory does not already exist
        cur_path = os.path.split(os.path.abspath(__file__))[0]
        output_fldr = 'images'
        output_dir = os.path.join(cur_path, output_fldr)
        if not os.access(output_dir, os.F_OK):
            os.makedirs(output_dir)

        # Plot steady-state consumption and savings distributions
        age_pers = np.arange(1, S + 1)
        fig, ax = plt.subplots()
        plt.plot(age_pers, c_ss, marker='D', label='Consumption')
        plt.plot(age_pers, b_splus1_ss, marker='D', label='Savings')
        # for the minor ticks, use no labels; default NullFormatter
        minorLocator = MultipleLocator(1)
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        # plt.title('Steady-state consumption and savings', fontsize=20)
        plt.xlabel(r'Age $s$')
        plt.ylabel(r'Units of consumption')
        plt.xlim((0, S + 1))
        # plt.ylim((-1.0, 1.15 * (b_ss.max())))
        plt.legend(loc='upper left')
        output_path = os.path.join(output_dir, 'SS_bc')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

        # Plot steady-state labor supply distributions
        fig, ax = plt.subplots()
        plt.plot(age_pers, n_ss, marker='D', label='Labor supply')
        # for the minor ticks, use no labels; default NullFormatter
        minorLocator = MultipleLocator(1)
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        # plt.title('Steady-state labor supply', fontsize=20)
        plt.xlabel(r'Age $s$')
        plt.ylabel(r'Labor supply')
        plt.xlim((0, S + 1))
        # plt.ylim((-0.1, 1.15 * (n_ss.max())))
        plt.legend(loc='upper right')
        output_path = os.path.join(output_dir, 'SS_n')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

    return ss_output
예제 #9
0
def get_SS_bsct(init_vals, args, graphs=False):
    '''
    --------------------------------------------------------------------
    Solve for the steady-state solution of the S-period-lived agent OG
    model with endogenous labor supply using the bisection method for
    the outer loop
    --------------------------------------------------------------------
    INPUTS:
    init_vals = length 3 tuple, (Kss_init, Lss_init, c1_init)
    args      = length 15 tuple, (J, S, lambdas, emat, beta, sigma,
                l_tilde, b_ellip, upsilon, chi_n_vec, A, alpha, delta,
                SS_tol, EulDiff)
    graphs    = boolean, =True if output steady-state graphs

    OTHER FUNCTIONS AND FILES CALLED BY THIS FUNCTION:
        firms.get_r()
        firms.get_w()
        hh.bn_solve()
        hh.c1_bSp1err()
        hh.get_cnb_vecs()
        aggr.get_K()
        aggr.get_L()
        aggr.get_Y()
        aggr.get_C()
        hh.get_cons()
        hh.get_n_errors()
        hh.get_b_errors()
        utils.print_time()

    OBJECTS CREATED WITHIN FUNCTION:
    start_time   = scalar > 0, clock time at beginning of program
    Kss_init     = scalar > 0, initial guess for steady-state aggregate
                   capital stock
    Lss_init     = scalar > 0, initial guess for steady-state aggregate
                   labor
    rss_init     = scalar > 0, initial guess for steady-state interest
                   rate
    wss_init     = scalar > 0, initial guess for steady-state wage
    c1_init      = scalar > 0, initial guess for first period consumpt'n
    S            = integer in [3, 80], number of periods an individual
                   lives
    beta         = scalar in (0,1), discount factor for each model per
    sigma        = scalar > 0, coefficient of relative risk aversion
    l_tilde      = scalar > 0, time endowment for each agent each period
    b_ellip      = scalar > 0, fitted value of b for elliptical
                   disutility of labor
    upsilon      = scalar > 1, fitted value of upsilon for elliptical
                   disutility of labor
    chi_n_vec    = (S,) vector, values for chi^n_s
    A            = scalar > 0, total factor productivity parameter in
                   firms' production function
    alpha        = scalar in (0,1), capital share of income
    delta        = scalar in [0,1], model-period depreciation rate of
                   capital
    SS_tol       = scalar > 0, tolerance level for steady-state fsolve
    EulDiff      = Boolean, =True if want difference version of Euler
                   errors beta*(1+r)*u'(c2) - u'(c1), =False if want
                   ratio version [beta*(1+r)*u'(c2)]/[u'(c1)] - 1
    hh_fsolve    = boolean, =True if solve inner-loop household problem
                   by choosing c_1 to set final period savings b_{S+1}=0
    KL_outer     = boolean, =True if guess K and L in outer loop
                   Otherwise, guess r and w in outer loop
    maxiter_SS   = integer >= 1, maximum number of iterations in outer
                   loop bisection method
    iter_SS      = integer >= 0, index of iteration number
    mindist_SS   = scalar > 0, minimum distance tolerance for
                   convergence
    dist_SS      = scalar > 0, distance metric for current iteration
    xi_SS        = scalar in (0,1], updating parameter
    KL_init      = (2,) vector, (K_init, L_init)
    c1_options   = length 1 dict, options to pass into
                   opt.root(c1_bSp1err,...)
    cnb_args     = length 8 tuple, args to pass into get_cnb_vecs()
    r_params     = length 3 tuple, args to pass into get_r()
    w_params     = length 2 tuple, args to pass into get_w()
    K_init       = scalar, initial value of aggregate capital stock
    L_init       = scalar, initial value of aggregate labor
    r_init       = scalar, initial value for interest rate
    w_init       = scalar, initial value for wage
    rpath        = (S,) vector, lifetime path of interest rates
    wpath        = (S,) vector, lifetime path of wages
    c1_args      = length 10 tuple, args to pass into c1_bSp1err()
    results_c1   = results object, root finder results from
                   opt.root(c1_bSp1err,...)
    c1_new       = scalar, updated value of optimal c1 given r_init and
                   w_init
    cvec_new     = (S,) vector, updated values for lifetime consumption
    nvec_new     = (S,) vector, updated values for lifetime labor supply
    bvec_new     = (S,) vector, updated values for lifetime savings
                   (b1, b2,...bS)
    b_Sp1_new    = scalar, updated value for savings in last period,
                   should be arbitrarily close to zero
    K_new        = scalar, updated K given bvec_new
    K_cnstr      = boolean, =True if K_new <= 0
    L_new        = scalar, updated L given nvec_new
    KL_new       = (2,) vector, updated K and L given bvec_new, nvec_new
    K_ss         = scalar > 0, steady-state aggregate capital stock
    L_ss         = scalar > 0, steady-state aggregate labor
    r_ss         = scalar > 0, steady-state interest rate
    w_ss         = scalar > 0, steady-state wage
    c1_ss        = scalar > 0, steady-state consumption in first period
    c_ss         = (S,) vector, steady-state lifetime consumption
    n_ss         = (S,) vector, steady-state lifetime labor supply
    b_ss         = (S,) vector, steady-state lifetime savings
                   (b1_ss, b2_ss, ...bS_ss) where b1_ss=0
    b_Sp1_ss     = scalar, steady-state savings for period after last
                   period of life. b_Sp1_ss approx. 0 in equilibrium
    Y_params     = length 2 tuple, (A, alpha)
    Y_ss         = scalar > 0, steady-state aggregate output (GDP)
    C_ss         = scalar > 0, steady-state aggregate consumption
    n_err_params = length 5 tuple, args to pass into get_n_errors()
    n_err_ss     = (S,) vector, lifetime labor supply Euler errors
    b_err_params = length 2 tuple, args to pass into get_b_errors()
    b_err_ss     = (S-1) vector, lifetime savings Euler errors
    RCerr_ss     = scalar, resource constraint error
    ss_time      = scalar, seconds elapsed to run steady-state comput'n
    ss_output    = length 14 dict, steady-state objects {n_ss, b_ss,
                   c_ss, b_Sp1_ss, w_ss, r_ss, K_ss, L_ss, Y_ss, C_ss,
                   n_err_ss, b_err_ss, RCerr_ss, ss_time}

    FILES CREATED BY THIS FUNCTION:
        SS_bc.png
        SS_n.png

    RETURNS: ss_output
    --------------------------------------------------------------------
    '''
    start_time = time.clock()
    Kss_init, Lss_init, c1_init = init_vals
    (J, S, lambdas, emat, beta, sigma, l_tilde, b_ellip, upsilon,
        chi_n_vec, A, alpha, delta, SS_tol, EulDiff) = args
    maxiter_SS = 200
    iter_SS = 0
    mindist_SS = 1e-12
    dist_SS = 10
    xi_SS = 0.2
    KL_init = np.array([Kss_init, Lss_init])
    c1_options = {'maxiter': 500}
    r_params = (A, alpha, delta)
    w_params = (A, alpha)
    while (iter_SS < maxiter_SS) and (dist_SS >= mindist_SS):
        iter_SS += 1
        K_init, L_init = KL_init
        r_init = firms.get_r(r_params, K_init, L_init)
        w_init = firms.get_w(w_params, K_init, L_init)
        rpath = r_init * np.ones(S)
        wpath = w_init * np.ones(S)
        cmat = np.zeros((S, J))
        nmat = np.zeros((S, J))
        bmat = np.zeros((S, J))
        b_Sp1_vec = np.zeros(J)
        for j in range(J):
            c1_args = (0.0, emat[:, j], beta, sigma, l_tilde, b_ellip,
                       upsilon, chi_n_vec, rpath, wpath, EulDiff)
            results_c1 = \
                opt.root(hh.c1_bSp1err, c1_init, args=(c1_args),
                         method='lm', tol=SS_tol, options=(c1_options))
            c1 = results_c1.x
            cnb_args = (0.0, emat[:, j], beta, sigma, l_tilde, b_ellip,
                        upsilon, chi_n_vec, EulDiff)
            cmat[:, j], nmat[:, j], bmat[:, j], b_Sp1_vec[j] = \
                hh.get_cnb_vecs(c1, rpath, wpath, cnb_args)
        K_new, K_cnstr = aggr.get_K(bmat, lambdas)
        L_new = aggr.get_L(nmat, emat, lambdas)
        KL_new = np.array([K_new, L_new])
        dist_SS = ((KL_new - KL_init) ** 2).sum()
        KL_init = xi_SS * KL_new + (1 - xi_SS) * KL_init
        print('SS Iteration=', iter_SS, ', SS Distance=',
              '%10.4e' % (dist_SS), ',K:', '%10.4e' % (K_new),
              'L:', '%10.4e' % (L_new))

    K_ss, L_ss = KL_init
    r_ss = firms.get_r(r_params, K_ss, L_ss)
    w_ss = firms.get_w(w_params, K_ss, L_ss)
    c_ss = cmat
    n_ss = nmat
    b_ss = bmat
    b_Sp1_ss = b_Sp1_vec
    Y_params = (A, alpha)
    Y_ss = aggr.get_Y(Y_params, K_ss, L_ss)
    C_ss = aggr.get_C(c_ss, lambdas)
    n_err_ss = np.zeros((S, J))
    b_err_ss = np.zeros((S - 1, J))
    for j in range(J):
        n_err_params = (emat[:, j], sigma, l_tilde, chi_n_vec, b_ellip,
                        upsilon)
        n_err_ss[:, j] = hh.get_n_errors(n_err_params, w_ss, c_ss[:, j],
                                         n_ss[:, j], EulDiff)
        b_err_params = (beta, sigma)
        b_err_ss[:, j] = hh.get_b_errors(b_err_params, r_ss, c_ss[:, j],
                                         EulDiff)
    RCerr_ss = Y_ss - C_ss - delta * K_ss

    ss_time = time.clock() - start_time

    ss_output = {
        'n_ss': n_ss, 'b_ss': b_ss, 'c_ss': c_ss, 'b_Sp1_ss': b_Sp1_ss,
        'w_ss': w_ss, 'r_ss': r_ss, 'K_ss': K_ss, 'L_ss': L_ss,
        'Y_ss': Y_ss, 'C_ss': C_ss, 'n_err_ss': n_err_ss,
        'b_err_ss': b_err_ss, 'RCerr_ss': RCerr_ss, 'ss_time': ss_time}
    print('K_ss=', K_ss, ', L_ss=', L_ss)
    print('r_ss=', r_ss, ', w_ss=', w_ss)
    print('Maximum abs. labor supply Euler error is: ',
          np.absolute(n_err_ss).max())
    print('Maximum abs. savings Euler error is: ',
          np.absolute(b_err_ss).max())
    print('Resource constraint error is: ', RCerr_ss)
    print('Max. absolute SS residual savings b_Sp1_j is: ',
          np.absolute(b_Sp1_ss).max())

    # Print SS computation time
    utils.print_time(ss_time, 'SS')

    if graphs:
        '''
        ----------------------------------------------------------------
        cur_path    = string, path name of current directory
        output_fldr = string, folder in current path to save files
        output_dir  = string, total path of images folder
        output_path = string, path of file name of figure to be saved
        sgrid       = (S,) vector, ages from 1 to S
        lamcumsum   = (J,) vector, cumulative sum of lambdas vector
        jmidgrid    = (J,) vector, midpoints of ability percentile bins
        smat        = (J, S) matrix, sgrid copied down J rows
        jmat        = (J, S) matrix, jmidgrid copied across S columns
        ----------------------------------------------------------------
        '''
        # Create directory if images directory does not already exist
        cur_path = os.path.split(os.path.abspath(__file__))[0]
        output_fldr = 'images'
        output_dir = os.path.join(cur_path, output_fldr)
        if not os.access(output_dir, os.F_OK):
            os.makedirs(output_dir)

        # Plot 3D steady-state consumption distribution
        sgrid = np.arange(1, S + 1)
        lamcumsum = lambdas.cumsum()
        jmidgrid = 0.5 * lamcumsum + 0.5 * (lamcumsum - lambdas)
        smat, jmat = np.meshgrid(sgrid, jmidgrid)
        cmap_c = cm.get_cmap('summer')
        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.set_xlabel(r'age-$s$')
        ax.set_ylabel(r'ability-$j$')
        ax.set_zlabel(r'indiv. consumption $c_{j,s}$')
        ax.plot_surface(smat, jmat, c_ss.T, rstride=1,
                        cstride=6, cmap=cmap_c)
        output_path = os.path.join(output_dir, 'c_ss_3D')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

        # Plot 2D steady-state consumption distribution
        minorLocator = MultipleLocator(1)
        fig, ax = plt.subplots()
        linestyles = np.array(["-", "--", "-.", ":"])
        markers = np.array(["x", "v", "o", "d", ">", "|"])
        pct_lb = 0
        for j in range(J):
            this_label = (str(int(np.rint(pct_lb))) + " - " +
                          str(int(np.rint(pct_lb + 100 * lambdas[j]))) +
                          "%")
            pct_lb += 100 * lambdas[j]
            if j <= 3:
                ax.plot(sgrid, c_ss[:, j], label=this_label,
                        linestyle=linestyles[j], color='black')
            elif j > 3:
                ax.plot(sgrid, c_ss[:, j], label=this_label,
                        marker=markers[j - 4], color='black')
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        box = ax.get_position()
        ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
        ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
        ax.set_xlabel(r'age-$s$')
        ax.set_ylabel(r'indiv. consumption $c_{j,s}$')
        output_path = os.path.join(output_dir, 'c_ss_2D')
        plt.savefig(output_path)
        # plt.show()
        plt.close()


        # Plot 3D steady-state labor supply distribution
        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.set_xlabel(r'age-$s$')
        ax.set_ylabel(r'ability-$j$')
        ax.set_zlabel(r'labor supply $n_{j,s}$')
        ax.plot_surface(smat, jmat, n_ss.T, rstride=1,
                        cstride=6, cmap=cmap_c)
        output_path = os.path.join(output_dir, 'n_ss_3D')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

        # Plot 2D steady-state labor supply distribution
        minorLocator = MultipleLocator(1)
        fig, ax = plt.subplots()
        linestyles = np.array(["-", "--", "-.", ":"])
        markers = np.array(["x", "v", "o", "d", ">", "|"])
        pct_lb = 0
        for j in range(J):
            this_label = (str(int(np.rint(pct_lb))) + " - " +
                          str(int(np.rint(pct_lb + 100 * lambdas[j]))) +
                          "%")
            pct_lb += 100 * lambdas[j]
            if j <= 3:
                ax.plot(sgrid, n_ss[:, j], label=this_label,
                        linestyle=linestyles[j], color='black')
            elif j > 3:
                ax.plot(sgrid, n_ss[:, j], label=this_label,
                        marker=markers[j - 4], color='black')
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        box = ax.get_position()
        ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
        ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
        ax.set_xlabel(r'age-$s$')
        ax.set_ylabel(r'labor supply $n_{j,s}$')
        output_path = os.path.join(output_dir, 'n_ss_2D')
        plt.savefig(output_path)
        # plt.show()
        plt.close()


        # Plot 3D steady-state savings/wealth distribution
        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.set_xlabel(r'age-$s$')
        ax.set_ylabel(r'ability-$j$')
        ax.set_zlabel(r'indiv. savings $b_{j,s}$')
        ax.plot_surface(smat, jmat, b_ss.T, rstride=1,
                        cstride=6, cmap=cmap_c)
        output_path = os.path.join(output_dir, 'b_ss_3D')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

        # Plot 2D steady-state savings/wealth distribution
        fig, ax = plt.subplots()
        linestyles = np.array(["-", "--", "-.", ":"])
        markers = np.array(["x", "v", "o", "d", ">", "|"])
        pct_lb = 0
        for j in range(J):
            this_label = (str(int(np.rint(pct_lb))) + " - " +
                          str(int(np.rint(pct_lb + 100 * lambdas[j]))) +
                          "%")
            pct_lb += 100 * lambdas[j]
            if j <= 3:
                ax.plot(sgrid, b_ss[:, j], label=this_label,
                        linestyle=linestyles[j], color='black')
            elif j > 3:
                ax.plot(sgrid, b_ss[:, j], label=this_label,
                        marker=markers[j - 4], color='black')
        ax.xaxis.set_minor_locator(minorLocator)
        plt.grid(b=True, which='major', color='0.65', linestyle='-')
        box = ax.get_position()
        ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
        ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
        ax.set_xlabel(r'age-$s$')
        ax.set_ylabel(r'indiv. savings $b_{j,s}$')
        output_path = os.path.join(output_dir, 'b_ss_2D')
        plt.savefig(output_path)
        # plt.show()
        plt.close()

    return ss_output