def inv_mn_and_v(c, d, a, b, w, par):

    v = utility.func(c, par) + w
    m = a + c + d
    n = b - d - pens.func(d, par)

    return m, n, v
def deviate_d_con(valt, n, c, a, w, par):

    for i_b in range(par.Nb_pd):
        for i_a in range(par.Na_pd):

            # a. choices
            d_x = par.delta_con * c[i_b, i_a]
            c_x = (1.0 - par.delta_con) * c[i_b, i_a]

            # b. post-decision states
            b_x = n[i_b, i_a] + d_x + pens.func(d_x, par)

            if not np.imag(b_x) == 0:
                valt[i_b, i_a] = np.nan
                continue

            # c. value
            w_x = linear_interp.interp_2d(par.grid_b_pd, par.grid_a_pd, w, b_x,
                                          a[i_b, i_a])
            v_x = utility.func(c_x, par) + w_x

            if not np.imag(v_x) == 0:
                valt[i_b, i_a] = np.nan
            else:
                valt[i_b, i_a] = v_x
Esempio n. 3
0
def obj_last_period(d, x, par):
    """ objective function in last period """

    # implied consumption (rest)
    c = x - d

    return -utility.func(c, d, par)
Esempio n. 4
0
def obj_bellman(c, p, m, v_plus, par):
    """ evaluate bellman equation """

    # a. end-of-period assets
    a = m - c

    # b. continuation value
    w = 0
    for ishock in range(par.Nshocks):

        # i. shocks
        psi = par.psi[ishock]
        psi_w = par.psi_w[ishock]
        xi = par.xi[ishock]
        xi_w = par.xi_w[ishock]

        # ii. next-period states
        p_plus = p * psi
        y_plus = p_plus * xi
        m_plus = par.R * a + y_plus

        # iii. weight
        weight = psi_w * xi_w

        # iv. interpolate
        w += weight * par.beta * linear_interp.interp_2d(
            par.grid_p, par.grid_m, v_plus, p_plus, m_plus)

    # c. total value
    value_of_choice = utility.func(c, par) + w

    return -value_of_choice  # we are minimizing
Esempio n. 5
0
def solve(sol, par, G2EGM=True):

    # unpack
    t = par.T - 1
    c = sol.c[t]
    d = sol.d[t]
    inv_v = sol.inv_v[t]
    inv_vm = sol.inv_vm[t]
    if G2EGM:
        inv_vn = sol.inv_vn[t]

    for i_n in range(par.Nn):

        # i. states
        m = par.grid_m
        n = par.grid_n[i_n]

        # ii. consume everything
        d[i_n, :] = 0
        c[i_n, :] = m + n

        # iii. value function
        v = utility.func(c[i_n, :], par)
        inv_v[i_n, :] = -1.0 / v

        # iv. value function derivatives
        vm = utility.marg_func(c[i_n, :], par)
        inv_vm[i_n, :] = 1.0 / vm
        if G2EGM:
            inv_vn[i_n, :] = inv_vm[i_n, :]
def calc_utility(sim, sol, par):
    """ calculate utility for each individual """

    # unpack
    u = sim.utility

    for t in range(par.simT):
        for i in prange(par.simN):

            u[i] += par.beta**t * utility.func(sim.c[t, i], sim.d[t, i], par)
def obj_bellman(c, m, interp_w, par):
    """ evaluate bellman equation """

    # a. end-of-period assets
    a = m - c

    # b. continuation value
    w = linear_interp.interp_1d(par.grid_a, interp_w, a)

    # c. total value
    value_of_choice = utility.func(c, par) + w

    return -value_of_choice  # we are minimizing
Esempio n. 8
0
def _egm(model, t, i_p, i_n):

    # a. unpack
    par = model.par
    sol = model.sol

    # b. figure
    fig = plt.figure(figsize=(12, 6))
    ax_c = fig.add_subplot(1, 2, 1)
    ax_v = fig.add_subplot(1, 2, 2)

    # c. plot before
    c_vec = sol.q_c[t, i_p, i_n]
    m_vec = sol.q_m[t, i_p, i_n]
    ax_c.plot(m_vec, c_vec, 'o', MarkerSize=0.5, label='before')
    ax_c.set_title(
        f'$c$ ($t = {t}$, $p = {par.grid_p[i_p]:.2f}$, $n = {par.grid_n[i_n]:.2f}$)',
        pad=10)

    inv_v_vec = np.zeros(par.Na)
    for i_a in range(par.Na):
        inv_v_vec[i_a] = utility.func(c_vec[i_a], par.grid_n[i_n],
                                      par) + (-1 / sol.inv_w[t, i_p, i_n, i_a])
    inv_v_vec = -1.0 / inv_v_vec

    ax_v.plot(m_vec, inv_v_vec, 'o', MarkerSize=0.5, label='before')
    ax_v.set_title(
        f'neg. inverse $v$ ($t = {t}$, $p = {par.grid_p[i_p]:.2f}$, $n = {par.grid_n[i_n]:.2f}$)',
        pad=10)

    # d. plot after
    c_vec = sol.c_keep[t, i_p, i_n, :]
    ax_c.plot(par.grid_m, c_vec, 'o', MarkerSize=0.5, label='after')

    inv_v_vec = sol.inv_v_keep[t, i_p, i_n, :]
    ax_v.plot(par.grid_m, inv_v_vec, 'o', MarkerSize=0.5, label='after')

    # e. details
    ax_c.legend()
    ax_c.set_ylabel('$c_t$')
    ax_c.set_ylim([c_vec[0], c_vec[-1]])
    ax_v.set_ylim([inv_v_vec[0], inv_v_vec[-1]])
    for ax in [ax_c, ax_v]:
        ax.grid(True)
        ax.set_xlabel('$m_t$')
        ax.set_xlim([par.grid_m[0], par.grid_m[-1]])

    plt.show()
def value_of_choice(t, c, d, p, x, inv_v_keep, inv_v_adj, par):

    # a. end-of-period-assets
    a = x - c - d

    # b. continuation value
    w = 0
    for ishock in range(par.Nshocks):

        # i. shocks
        psi = par.psi[ishock]
        psi_w = par.psi_w[ishock]
        xi = par.xi[ishock]
        xi_w = par.xi_w[ishock]

        # ii. next-period states
        p_plus = trans.p_plus_func(p, psi, par)
        n_plus = trans.n_plus_func(d, par)
        m_plus = trans.m_plus_func(a, p_plus, xi, par)
        x_plus = trans.x_plus_func(m_plus, n_plus, par)

        # iii. weight
        weight = psi_w * xi_w

        # iv. update
        inv_v_plus_keep_now = linear_interp.interp_3d(par.grid_p, par.grid_n,
                                                      par.grid_m,
                                                      inv_v_keep[t + 1],
                                                      p_plus, n_plus, m_plus)

        inv_v_plus_adj_now = linear_interp.interp_2d(par.grid_p, par.grid_x,
                                                     inv_v_adj[t + 1], p_plus,
                                                     x_plus)

        v_plus_now = -np.inf  # huge negative value

        if inv_v_plus_keep_now > inv_v_plus_adj_now and inv_v_plus_keep_now > 0:
            v_plus_now = -1 / inv_v_plus_keep_now
        elif inv_v_plus_adj_now > 0:
            v_plus_now = -1 / inv_v_plus_adj_now

        w += weight * par.beta * v_plus_now

    # v. total value
    v = utility.func(c, d, par) + w

    return v  # we are minimizing
Esempio n. 10
0
def solve_outer(t,sol,par):

    # unpack output
    inv_v = sol.inv_v[t]
    inv_vm = sol.inv_vm[t]
    c = sol.c[t]
    d = sol.d[t]

    # loop over outer states
    for i_n in range(par.Nn):
            
        n = par.grid_n[i_n]

        # loop over m state
        for i_m in range(par.Nm):
            
            m = par.grid_m[i_m]
            
            # a. optimal choice
            d_low = 1e-8
            d_high = m-1e-8
            d[i_n,i_m] = golden_section_search.optimizer(obj_outer,d_low,d_high,args=(n,m,t,sol,par),tol=1e-8)

            # b. optimal value
            n_pure_c = n + d[i_n,i_m] + pens.func(d[i_n,i_m],par)
            m_pure_c = m - d[i_n,i_m]
            c[i_n,i_m] = np.fmin(linear_interp.interp_2d(par.grid_b_pd,par.grid_l,sol.c_pure_c[t],n_pure_c,m_pure_c),m_pure_c)
            inv_v[i_n,i_m] = -obj_outer(d[i_n,i_m],n,m,t,sol,par)

            # c. dcon
            obj_dcon = -obj_outer(0,n,m,t,sol,par)
            if obj_dcon > inv_v[i_n,i_m]:
                c[i_n,i_m] = linear_interp.interp_2d(par.grid_b_pd,par.grid_l,sol.c_pure_c[t],n,m)
                d[i_n,i_m] = 0
                inv_v[i_n,i_m] = obj_dcon

            # d. con
            w = linear_interp.interp_2d(par.grid_b_pd,par.grid_a_pd,sol.w[t],n,0)
            obj_con = -1.0/(utility.func(m,par) + w)
            if obj_con > inv_v[i_n,i_m]:
                c[i_n,i_m] = m
                d[i_n,i_m] = 0
                inv_v[i_n,i_m] = obj_con

            # e. derivative
            inv_vm[i_n,i_m] = 1.0/utility.marg_func(c[i_n,i_m],par)
def solve(t,ma,st,ra,d,sol_c,sol_m,sol_v,par):
    """ solve the model in the last period for singles """
        
    # unpack (helps numba optimize)
    c = sol_c[t,ra,d,:]
    m = sol_m[:]
    v = sol_v[t,ra,d,:]

    # initialize
    c[:] = m[:]
    
    # optimal choice
    cons = (par.beta*par.gamma)**(-1/par.rho)
    for i in range(len(m)):
        if m[i] > cons:
            c[i] = cons
        else:
            c[i] = m[i]
        v[i] = utility.func(c[i],d,ma,st,par) + par.beta*par.gamma*(m[i]-c[i])
Esempio n. 12
0
def solve(t, sol, par):
    """ solve the problem in the last period """

    # unpack (helps numba optimize)
    v = sol.v[t]
    c = sol.c[t]

    # loop over states
    for ip in prange(par.Np):  # in parallel
        for im in range(par.Nm):

            # a. states
            _p = par.grid_p[ip]
            m = par.grid_m[im]

            # b. optimal choice (consume everything)
            c[ip, im] = m

            # c. optimal value
            v[ip, im] = utility.func(c[ip, im], par)
def solve_con(out_c,out_d,out_v,w,par):
                        
    # i. choices
    c = par.grid_m_nd
    d = np.zeros(c.shape)
        
    # ii. post-decision states
    a = np.zeros(c.shape)
    b = par.grid_n_nd

    # iii. post decision value
    w_con = np.zeros(c.shape)
    linear_interp.interp_2d_vec(par.grid_b_pd,par.grid_a_pd,w,b.ravel(),a.ravel(),w_con.ravel())

    # iv. value
    v = utility.func(c,par) + w_con     

    out_c[:] = c
    out_d[:] = d
    out_v[:] = v
Esempio n. 14
0
def solve(t, sol, par):
    """ solve the problem in the last period """

    # unpack
    inv_v_keep = sol.inv_v_keep[t]
    inv_marg_u_keep = sol.inv_marg_u_keep[t]
    c_keep = sol.c_keep[t]
    inv_v_adj = sol.inv_v_adj[t]
    inv_marg_u_adj = sol.inv_marg_u_adj[t]
    d_adj = sol.d_adj[t]
    c_adj = sol.c_adj[t]

    # a. keep
    for i_p in prange(par.Np):
        for i_n in range(par.Nn):
            for i_m in range(par.Nm):

                # i. states
                n = par.grid_n[i_n]
                m = par.grid_m[i_m]

                if m == 0:  # forced c = 0
                    c_keep[i_p, i_n, i_m] = 0
                    inv_v_keep[i_p, i_n, i_m] = 0
                    inv_marg_u_keep[i_p, i_n, i_m] = 0
                    continue

                # ii. optimal choice
                c_keep[i_p, i_n, i_m] = m

                # iii. optimal value
                v_keep = utility.func(c_keep[i_p, i_n, i_m], n, par)
                inv_v_keep[i_p, i_n, i_m] = -1.0 / v_keep
                inv_marg_u_keep[i_p, i_n, i_m] = 1.0 / utility.marg_func(
                    c_keep[i_p, i_n, i_m], n, par)

    # b. adj
    for i_p in prange(par.Np):
        for i_x in range(par.Nx):

            # i. states
            x = par.grid_x[i_x]

            if x == 0:  # forced c = d = 0
                d_adj[i_p, i_x] = 0
                c_adj[i_p, i_x] = 0
                inv_v_adj[i_p, i_x] = 0
                inv_marg_u_adj[i_p, i_x] = 0
                continue

            # ii. optimal choices
            d_low = np.fmin(x / 2, 1e-8)
            d_high = np.fmin(x, par.n_max)
            d_adj[i_p, i_x] = golden_section_search.optimizer(
                d_low, d_high, par.tol, obj_last_period, x, par)
            c_adj[i_p, i_x] = x - d_adj[i_p, i_x]

            # iii. optimal value
            v_adj = -obj_last_period(d_adj[i_p, i_x], x, par)
            inv_v_adj[i_p, i_x] = -1.0 / v_adj
            inv_marg_u_adj[i_p, i_x] = 1.0 / utility.marg_func(
                c_adj[i_p, i_x], d_adj[i_p, i_x], par)
def upperenvelope(out_c, out_d, out_v, holes, i_a, i_b, tri, m, n, c, d, v, Na,
                  Nb, valid, num, w, par):

    # a. simplex in (a,b)-space (or similar with constrained choices)
    i_b_1 = i_b
    i_a_1 = i_a

    if i_b == Nb - 1: return
    i_b_2 = i_b + 1
    i_a_2 = i_a

    i_b_3 = -1  # to be overwritten
    i_a_3 = -1  # to be overwritten

    if tri == 0:
        if i_a == 0 or i_b == Nb - 1: return
        i_b_3 = i_b + 1
        i_a_3 = i_a - 1
    else:
        if i_a == Na - 1: return
        i_b_3 = i_b
        i_a_3 = i_a + 1

    if ~valid[i_b_1, i_a_1] or ~valid[i_b_2, i_a_2] or ~valid[i_b_3, i_a_3]:
        return

    # b. simplex in (m,n)-space
    m1 = m[i_b_1, i_a_1]
    m2 = m[i_b_2, i_a_2]
    m3 = m[i_b_3, i_a_3]

    n1 = n[i_b_1, i_a_1]
    n2 = n[i_b_2, i_a_2]
    n3 = n[i_b_3, i_a_3]

    # c. boundary box values and indices in common grid
    m_max = np.fmax(m1, np.fmax(m2, m3))
    m_min = np.fmin(m1, np.fmin(m2, m3))
    n_max = np.fmax(n1, np.fmax(n2, n3))
    n_min = np.fmin(n1, np.fmin(n2, n3))

    im_low = 0
    if m_min >= 0:
        im_low = linear_interp.binary_search(0, par.Nm, par.grid_m, m_min)
    im_high = linear_interp.binary_search(0, par.Nm, par.grid_m, m_max) + 1

    in_low = 0
    if n_min >= 0:
        in_low = linear_interp.binary_search(0, par.Nn, par.grid_n, n_min)
    in_high = linear_interp.binary_search(0, par.Nn, par.grid_n, n_max) + 1

    # correction to allow for more extrapolation
    im_low = np.fmax(im_low - par.egm_extrap_add, 0)
    im_high = np.fmin(im_high + par.egm_extrap_add, par.Nm)
    in_low = np.fmax(in_low - par.egm_extrap_add, 0)
    in_high = np.fmin(in_high + par.egm_extrap_add, par.Nn)

    # d. prepare barycentric interpolation
    denom = (n2 - n3) * (m1 - m3) + (m3 - m2) * (n1 - n3)

    # e. loop through common grid nodes in interior of bounding box
    for i_n in range(in_low, in_high):
        for i_m in range(im_low, im_high):

            # i. common grid values
            m_now = par.grid_m[i_m]
            n_now = par.grid_n[i_n]

            # ii. barycentric coordinates
            w1 = ((n2 - n3) * (m_now - m3) + (m3 - m2) * (n_now - n3)) / denom
            w2 = ((n3 - n1) * (m_now - m3) + (m1 - m3) * (n_now - n3)) / denom
            w3 = 1 - w1 - w2

            # iii. exit if too much outside simplex
            if w1 < par.egm_extrap_w or w2 < par.egm_extrap_w or w3 < par.egm_extrap_w:
                continue

            # iv. interpolate choices
            if num == 1:  # ucon, interpolate c and d

                c_interp = w1 * c[i_b_1, i_a_1] + w2 * c[
                    i_b_2, i_a_2] + w3 * c[i_b_3, i_a_3]
                d_interp = w1 * d[i_b_1, i_a_1] + w2 * d[
                    i_b_2, i_a_2] + w3 * d[i_b_3, i_a_3]
                a_interp = m_now - c_interp - d_interp
                b_interp = n_now + d_interp + pens.func(d_interp, par)

            elif num == 2:  # dcon, interpolate c

                c_interp = w1 * c[i_b_1, i_a_1] + w2 * c[
                    i_b_2, i_a_2] + w3 * c[i_b_3, i_a_3]
                d_interp = 0.0
                a_interp = m_now - c_interp - d_interp
                b_interp = n_now  # d_interp = 0

            elif num == 3:  # acon, interpolate d

                a_interp = 0.0
                d_interp = w1 * d[i_b_1, i_a_1] + w2 * d[
                    i_b_2, i_a_2] + w3 * d[i_b_3, i_a_3]
                c_interp = m_now - a_interp - d_interp
                b_interp = n_now + d_interp + pens.func(d_interp, par)

            if c_interp <= 0.0 or d_interp < 0.0 or a_interp < 0 or b_interp < 0:
                continue

            # v. value-of-choice
            w_interp = linear_interp.interp_2d(par.grid_b_pd, par.grid_a_pd, w,
                                               b_interp, a_interp)
            v_interp = utility.func(c_interp, par) + w_interp

            # vi. update if max
            if v_interp > out_v[i_n, i_m]:

                out_v[i_n, i_m] = v_interp
                out_c[i_n, i_m] = c_interp
                out_d[i_n, i_m] = d_interp
                holes[i_n, i_m] = 0
def fill_holes(out_c, out_d, out_v, holes, w, num, par):

    # a. locate global bounding box with content
    i_n_min = 0
    i_n_max = par.Nn - 1
    min_n = np.inf
    max_n = -np.inf

    i_m_min = 0
    i_m_max = par.Nm - 1
    min_m = np.inf
    max_m = -np.inf

    for i_n in range(par.Nn):
        for i_m in range(par.Nn):

            m_now = par.grid_m[i_m]
            n_now = par.grid_n[i_n]

            if holes[i_n, i_m] == 1: continue

            if m_now < min_m:
                min_m = m_now
                i_m_min = i_m

            if m_now > max_m:
                max_m = m_now
                i_m_max = i_m

            if n_now < min_n:
                min_n = n_now
                i_n_min = i_n

            if n_now > max_n:
                max_n = n_now
                i_n_max = i_n

    # b. loop through m, n, k nodes to detect holes
    i_n_max = np.fmin(i_n_max + 1, par.Nn)
    i_m_max = np.fmin(i_m_max + 1, par.Nm)
    for i_n in range(i_n_min, i_n_max):
        for i_m in range(i_m_min, i_m_max):

            if holes[i_n, i_m] == 0:  # if not hole
                continue

            m_now = par.grid_m[i_m]
            n_now = par.grid_n[i_n]
            m_add = 2
            n_add = 2

            # loop over points close by
            i_n_close_min = np.fmax(0, i_n - n_add)
            i_n_close_max = np.fmin(i_n + n_add + 1, par.Nn)

            i_m_close_min = np.fmax(0, i_m - m_add)
            i_m_close_max = np.fmin(i_m + m_add + 1, par.Nm)

            for i_n_close in range(i_n_close_min, i_n_close_max):
                for i_m_close in range(i_m_close_min, i_m_close_max):

                    if holes[i_n_close, i_m_close] == 1:  # if itself a hole
                        continue

                    if num == 1:  # ucon, interpolate c and d

                        c_interp = out_c[i_n_close, i_m_close]
                        d_interp = out_d[i_n_close, i_m_close]
                        a_interp = m_now - c_interp - d_interp
                        b_interp = n_now + d_interp + par.chi * np.log(
                            1.0 + d_interp)

                    elif num == 2:  # dcon, interpolate c

                        c_interp = out_c[i_n_close, i_m_close]
                        d_interp = 0.0
                        a_interp = m_now - c_interp - d_interp
                        b_interp = n_now  # d_interp = 0

                    elif num == 3:  # acon, interpolate d

                        a_interp = 0.0
                        d_interp = out_d[i_n_close, i_m_close]
                        c_interp = m_now - a_interp - d_interp
                        b_interp = n_now + d_interp + par.chi * np.log(
                            1.0 + d_interp)

                    if c_interp <= 0.0 or d_interp < 0.0 or a_interp < 0 or b_interp < 0:
                        continue

                    # value-of-choice
                    w_interp = linear_interp.interp_2d(par.grid_b_pd,
                                                       par.grid_a_pd, w,
                                                       b_interp, a_interp)
                    v_interp = utility.func(c_interp, par) + w_interp

                    # update if better
                    if v_interp > out_v[i_n, i_m]:

                        out_v[i_n, i_m] = v_interp
                        out_c[i_n, i_m] = c_interp
                        out_d[i_n, i_m] = d_interp