Esempio n. 1
0
    def compute_EV_scalar(self, istate, kp):
        # Unpack parameters
        A, alpha, beta, delta, gamma, rho, sigma = self._unpack_params()

        # All possible exogenous states tomorrow
        z1 = self.ncgm.z1[istate, :]
        phi = complete_polynomial(np.vstack([np.ones(self.ncgm.Qn) * kp, z1]),
                                  self.degree).T
        val = self.ncgm.weights @ (phi @ self.v_coeffs)

        return val
Esempio n. 2
0
def test_complete_vec_vs_mat():
    # Matrix for allocation
    temp = np.ones(n_complete(2, 3)) * 5.0
    temp_mat = np.ones((n_complete(2, 3), 3))

    # Point at which to evaluate
    z = np.array([0.9, 1.05])
    z_mat = np.array([[0.9, 0.95, 1.0], [1.05, 1.0, 0.95]])

    foo = complete_polynomial(z, 2)
    bar = complete_polynomial(z_mat, 2)[:, 0]
    assert np.allclose(foo, bar)

    foo = complete_polynomial_der(z, 2, 0)
    bar = complete_polynomial_der(z_mat, 2, 0)[:, 0]
    assert np.allclose(foo, bar)

    foo = complete_polynomial_der(z, 4, 0)
    bar = complete_polynomial_der(z_mat, 4, 0)[:, 0]
    assert np.allclose(foo, bar)
Esempio n. 3
0
    def __init__(self, ncgm, degree, prev_sol=None):
        # Save model and approximation degree
        self.ncgm, self.degree = ncgm, degree

        # Unpack some info from ncgm
        A, alpha, beta, delta, gamma, rho, sigma = self._unpack_params()
        grid = self.ncgm.grid
        k = grid[:, 0]
        z = grid[:, 1]

        # Use parameter values from model to create a namedtuple with
        # parameters saved inside
        self.params = Params(A, alpha, beta, delta, gamma, rho, sigma)

        self.Phi = complete_polynomial(grid.T, degree).T
        self.dPhi = complete_polynomial_der(grid.T, degree, 0).T

        # Update to fill initial value and policy matrices
        # If we give it another solution type then use it to
        # generate values and policies
        if issubclass(type(prev_sol), GeneralSolution):
            oldPhi = complete_polynomial(ncgm.grid.T, prev_sol.degree).T
            self.VF = oldPhi @ prev_sol.v_coeffs
            self.KP = oldPhi @ prev_sol.k_coeffs
        # If we give it a tuple then assume it is (policy, value) pair
        elif type(prev_sol) is tuple:
            self.KP = prev_sol[0]
            self.VF = prev_sol[1]
        # Otherwise guess a constant value function and a policy
        # of roughly steady state
        else:
            # VF is 0 everywhere
            self.VF = np.zeros(ncgm.ns)

            # Roughly ss policy
            c_pol = f(k, z, A, alpha) * (A - delta) / A
            self.KP = expendables_t(k, z, A, alpha, delta) - c_pol

        # Coefficients based on guesses
        self.v_coeffs = la.lstsq(self.Phi, self.VF)[0]
        self.k_coeffs = la.lstsq(self.Phi, self.KP)[0]
Esempio n. 4
0
    def compute_EV(self, kp=None):
        """
        Compute the expected value
        """
        # Unpack parameters
        A, alpha, beta, delta, gamma, rho, sigma = self._unpack_params()
        grid = self.ncgm.grid
        ns, Qn = self.ncgm.ns, self.ncgm.Qn

        # Use policy to compute kp and c
        if kp is None:
            kp = self.Phi @ self.k_coeffs

        # Evaluate E[V_{t+1}]
        Vtp1 = np.empty((Qn, grid.shape[0]))
        for iztp1 in range(Qn):
            grid_tp1 = np.vstack([kp, self.ncgm.z1[:, iztp1]])
            Phi_tp1 = complete_polynomial(grid_tp1, self.degree).T
            Vtp1[iztp1, :] = Phi_tp1 @ self.v_coeffs

        EV = self.ncgm.weights @ Vtp1

        return EV
Esempio n. 5
0
    def update(self):
        """
        Updates the coefficients and value functions using the VFI_ECM
        method
        """
        # Unpack parameters
        A, alpha, beta, delta, gamma, rho, sigma = self._unpack_params()
        ns = self.ncgm.ns
        grid = self.ncgm.grid

        # Use the grid as capital tomorrow instead of capital today
        k1 = grid[:, 0]
        z = grid[:, 1]
        dEV = self.compute_dEV(kp=k1)
        c = duinv(beta * dEV, gamma)

        # Now find corresponding capital today such that kp is optimal
        kt = np.empty(ns)
        for i in range(ns):
            ct = c[i]
            ktp1 = k1[i]
            zt = z[i]
            obj = lambda k: expendables_t(k, zt, A, alpha, delta) - ct - ktp1
            kt[i] = opt.bisect(obj, 0.25, 2.0)

        # Compute value today
        EV = self.compute_EV(kp=k1)
        V = u(c, gamma) + beta * EV

        # Get implied coefficients and evaluate kp and VF
        phi = complete_polynomial(np.vstack([kt, z]), self.degree).T
        temp_k_coeffs = la.lstsq(phi, k1)[0]
        temp_v_coeffs = la.lstsq(phi, V)[0]
        kp = self.Phi @ temp_k_coeffs
        VF = self.Phi @ temp_v_coeffs

        return kp, VF
Esempio n. 6
0
    def solve(self, damp=0.1, tol=1e-7, verbose=False):
        # rename self to m to make code below readable
        m = self

        n = len(m.g.ηR)
        n_nodes = len(m.g.ω_nodes)

        ## allocate memory
        # euler equations
        e = np.zeros((3, n))

        # previous iteration S, F, C
        S0_old_G = np.ones(n)
        F0_old_G = np.ones(n)
        C0_old_G = np.ones(n)

        # current iteration S, F, C
        S0_new_G = np.ones(n)
        F0_new_G = np.ones(n)
        C0_new_G = np.ones(n)

        # future S, F, C
        S1 = np.zeros((n_nodes, n))
        F1 = np.zeros((n_nodes, n))
        C1 = np.zeros((n_nodes, n))

        degs = [self.p.degree] if self.p.degree == 1 else [1, self.p.degree]

        for deg in degs:
            # housekeeping
            err = 1.0
            it = 0
            X0_G = m.g.X0_G[deg]

            start_time = time.time()

            if deg <= 2:
                coefs = self.init_coefs(deg)
            else:
                coefs = np.linalg.lstsq(X0_G.T, e.T)[0].T
                # old_coefs = coefs.copy()
                # coefs = self.init_coefs(deg)
                # coefs[:, :old_coefs.shape[1]] = old_coefs

            while err > tol:
                it += 1
                # Current choices (at t)
                # ------------------------------
                SFC0 = coefs @ X0_G
                S0 = SFC0[0, :]  # Compute S(t) using coefs
                F0 = SFC0[1, :]  # Compute F(t) using coefs
                C0 = (SFC0[2, :])**(-1 / m.p.γ)  # Compute C(t) using coefs
                π0, δ1, Y0, L0, Yn0, R1 = self.step(S0, F0, C0, m.g.δ, m.g.R,
                                                    m.g.ηG, m.g.ηa, m.g.ηL,
                                                    m.g.ηR)

                if self.p.zlb:
                    R1 = np.maximum(R1, 1.0)

                for u in range(n_nodes):
                    # Form complete polynomial of degree "Degree" (at t+1 states)
                    grid1 = [
                        np.log(R1),
                        np.log(δ1), m.g.ηR1[u, :], m.g.ηa1[u, :],
                        m.g.ηL1[u, :], m.g.ηu1[u, :], m.g.ηB1[u, :],
                        m.g.ηG1[u, :]
                    ]

                    X1 = complete_polynomial(grid1, deg)

                    S1[u, :] = coefs[0, :] @ X1  # Compute S(t+1)
                    F1[u, :] = coefs[1, :] @ X1  # Compute F(t+1)
                    C1[u, :] = (coefs[2, :] @ X1)**(-1 / m.p.γ
                                                    )  # Compute C(t+1)

                # Compute next-period π using condition
                # (35) in MM (2015)
                π1 = ((1 - (1 - m.p.Θ) * (S1 / F1)**(1 - m.p.ϵ)) /
                      m.p.Θ)**(1 / (m.p.ϵ - 1))

                # Evaluate conditional expectations in the Euler equations
                #---------------------------------------------------------
                e[0, :] = exp(m.g.ηu) * exp(m.g.ηL) * L0**m.p.ϑ * Y0 / exp(
                    m.g.ηa) + m.g.ω_nodes @ (m.p.β * m.p.Θ * π1**m.p.ϵ * S1)
                e[1, :] = exp(m.g.ηu) * C0**(-m.p.γ) * Y0 + m.g.ω_nodes @ (
                    m.p.β * m.p.Θ * π1**(m.p.ϵ - 1) * F1)
                e[2, :] = m.p.β * exp(m.g.ηB) / exp(m.g.ηu) * R1 * (
                    m.g.ω_nodes @ ((exp(m.g.ηu1) * C1**(-m.p.γ) / π1)))

                # Variables of the current iteration
                #-----------------------------------
                np.copyto(S0_new_G, S0)
                np.copyto(F0_new_G, F0)
                np.copyto(C0_new_G, C0)

                # Compute and update the coefficients of the decision functions
                # -------------------------------------------------------------
                coefs_hat = np.linalg.lstsq(X0_G.T, e.T)[0].T

                # Update the coefficients using damping
                coefs = damp * coefs_hat + (1 - damp) * coefs

                # Evaluate the percentage (unit-free) difference between the values
                # on the grid from the previous and current iterations
                # -----------------------------------------------------------------
                # The convergence criterion is adjusted to the damping parameters
                err = (np.mean(np.abs(1 - S0_new_G / S0_old_G)) +
                       np.mean(np.abs(1 - F0_new_G / F0_old_G)) +
                       np.mean(np.abs(1 - C0_new_G / C0_old_G)))

                # Store the obtained values for S(t), F(t), C(t) on the grid to
                # be used on the subsequent iteration in Section 10.2.6
                #-----------------------------------------------------------------------
                np.copyto(S0_old_G, S0_new_G)
                np.copyto(F0_old_G, F0_new_G)
                np.copyto(C0_old_G, C0_new_G)

                if it % 20 == 0 and verbose:
                    print("On iteration {:d} err is {:6.7e}".format(it, err))

        elapsed = time.time() - start_time

        return coefs, elapsed
Esempio n. 7
0
    def __init__(self, p, kind="random", grid_source="mat"):
        m = p.grid_size
        σ = np.array([p.σηR, p.σηa, p.σηL, p.σηu, p.σηB, p.σηG])
        ρ = np.array([p.ρηR, p.ρηa, p.ρηL, p.ρηu, p.ρηB, p.ρηG])

        if kind == "sobol":
            if grid_source == "mat":
                _path = os.path.join(DIR, "Sobol_grids.mat")
                s = loadmat(_path)["Sobol_grids"][:m, :]
            else:
                s = sobol_seq.i4_sobol_generate(8, m)
            sη = s[:, :6]
            η = (-2 * σ + 4 * (sη.max(0) - sη) /
                 (sη.max(0) - sη.min(0)) * σ) / np.sqrt(1 - ρ**2)
            R = 1 + 0.05 * (np.max(s[:, 6]) - s[:, 6]) / (np.max(s[:, 6]) -
                                                          np.min(s[:, 6]))
            δ = 0.95 + 0.05 * (np.max(s[:, 7]) - s[:, 7]) / (np.max(s[:, 7]) -
                                                             np.min(s[:, 7]))
        else:
            # Values of exogenous state variables are distributed uniformly
            # in the interval +/- std/sqrt(1-rho_nu**2)
            if grid_source == "mat":
                _path = os.path.join(DIR, "random_grids.mat")
                s = loadmat(_path)["random_grids"][:m, :]
            else:
                s = np.random.rand(m, 8)
            sη = s[:, :6]
            η = (-2 * σ + 4 * σ * sη) / np.sqrt(1 - ρ**2)

            # Values of endogenous state variables are distributed uniformly
            # in the intervals [1 1.05] and [0.95 1], respectively
            R = 1 + 0.05 * s[:, 6]
            δ = 0.95 + 0.05 * s[:, 7]

        ηR = η[:, 0]
        ηa = η[:, 1]
        ηL = η[:, 2]
        ηu = η[:, 3]
        ηB = η[:, 4]
        ηG = η[:, 5]

        self.ηR = ηR
        self.ηa = ηa
        self.ηL = ηL
        self.ηu = ηu
        self.ηB = ηB
        self.ηG = ηG
        self.R = R
        self.δ = δ

        # shape (8, m)
        self.X = np.vstack([np.log(R), np.log(δ), η.T])

        # shape (n_complete(8, p.Degree), m)
        self.X0_G = {
            1: complete_polynomial(self.X, 1),
            p.degree: complete_polynomial(self.X, p.degree)
        }

        # shape (2*n=12, n=6)
        self.ϵ_nodes, self.ω_nodes = qnwmonomial1(p.vcov)

        # all shape (len(ϵ_nodes), m)
        self.ηR1 = p.ρηR * ηR[None, :] + self.ϵ_nodes[:, None, 0]
        self.ηa1 = p.ρηa * ηa[None, :] + self.ϵ_nodes[:, None, 1]
        self.ηL1 = p.ρηL * ηL[None, :] + self.ϵ_nodes[:, None, 2]
        self.ηu1 = p.ρηu * ηu[None, :] + self.ϵ_nodes[:, None, 3]
        self.ηB1 = p.ρηB * ηB[None, :] + self.ϵ_nodes[:, None, 4]
        self.ηG1 = p.ρηG * ηG[None, :] + self.ϵ_nodes[:, None, 5]
Esempio n. 8
0
def gssa(model, maxit=100, tol=1e-8, initial_dr=None, verbose=False,
         n_sim=10000, deg=3, damp=0.1, seed=42):
    """
    Sketch of algorithm:

    0. Choose levels for the initial states and the simulation length (n_sim)
    1. Obtain an initial decision rule -- here using first order perturbation
    2. Draw a sequence of innovations epsilon
    3. Iterate on the following steps:
        - Use the epsilons, initial states, and proposed decision rule to
          simulate model forward. Will leave us with time series of states and
          controls
        - Evaluate expectations using quadrature
        - Use direct response to get alternative proposal for controls
        - Regress updated controls on the simulated states to get proposal
          coefficients. New coefficients are convex combination of previous
          coefficients and proposal coefficients. Weights controlled by damp,
          where damp is the weight on the old coefficients. This should be
          fairly low to increase chances of convergence.
        - Check difference between the simulated series of controls and the
          direct response version of controls

    """
    # verify input arguments
    if deg < 0 or deg > 5:
        raise ValueError("deg must be in [1, 5]")

    if damp < 0 or damp > 1:
        raise ValueError("damp must be in [0, 1]")

    t1 = time.time()

    # extract model functions and parameters
    g = model.__original_functions__['transition']
    g_gu = model.__original_gufunctions__['transition']
    h_gu = model.__original_gufunctions__['expectation']
    d_gu = model.__original_gufunctions__['direct_response']
    p = model.calibration['parameters']
    n_s = len(model.symbols["states"])
    n_x = len(model.symbols["controls"])
    n_z = len(model.symbols["expectations"])
    n_eps = len(model.symbols["shocks"])
    s0 = model.calibration["states"]
    x0 = model.calibration["controls"]

    # construct initial decision rule if not supplied
    if initial_dr is None:
        drp = approximate_controls(model)
    else:
        drp = initial_dr

    # set up quadrature weights and nodes
    distrib = model.get_distribution()
    nodes, weights = distrib.discretize()

    # draw sequence of innovations
    np.random.seed(seed)
    distrib = model.get_distribution()
    sigma = distrib.sigma
    epsilon = np.random.multivariate_normal(np.zeros(n_eps), sigma, n_sim)

    # simulate initial decision rule and do initial regression for coefs
    init_sim = simulate(model, drp, horizon=n_sim, return_array=True,
                        forcing_shocks=epsilon)
    s_sim = init_sim[:, 0, 0:n_s]
    x_sim = init_sim[:, 0, n_s:n_s + n_x]
    Phi_sim = complete_polynomial(s_sim.T, deg).T
    coefs = np.ascontiguousarray(lstsq(Phi_sim, x_sim)[0])

    # NOTE: the ascontiguousarray above was needed for numba to compile the
    #       `np.dot` in the simulation function in no python mode. Appearantly
    #       the array returned from lstsq is not C-contiguous

    # allocate for simulated series of expectations and next period states
    z_sim = np.empty((n_sim, n_z))
    S = np.empty_like(s_sim)
    X = np.empty_like(x_sim)
    H = np.empty_like(z_sim)
    new_x = np.empty_like(x_sim)

    # set initial states and controls
    s_sim[0, :] = s0
    x_sim[0, :] = x0

    Phi_t = np.empty(n_complete(n_s, deg))  # buffer array for simulation

    # create jitted function that will simulate states and controls, using
    # the epsilon shocks from above (define here as closure over all data
    # above).
    @jit(nopython=True)
    def simulate_states_controls(s, x, Phi_t, coefs):
        for t in range(1, n_sim):
            g(s[t - 1, :], x[t - 1, :], epsilon[t, :], p, s[t, :])

            # fill Phi_t with new complete poly version of s[t, :]
            _complete_poly_impl_vec(s[t, :], deg, Phi_t)

            # do inner product to get new controls
            x[t, :] = Phi_t @coefs

    it = 0
    err = 10.0
    err_0 = 10

    if verbose:
        headline = '|{0:^4} | {1:10} | {2:8} | {3:8} |'
        headline = headline.format('N', ' Error', 'Gain', 'Time')
        stars = '-' * len(headline)
        print(stars)
        print(headline)
        print(stars)

        # format string for within loop
        fmt_str = '|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} |'

    while err > tol and it <= maxit:
        t_start = time.time()

        # simulate with new coefficients
        simulate_states_controls(s_sim, x_sim, Phi_t, coefs)

        # update expectations of z
        # update_expectations(s_sim, x_sim, z_sim, Phi_sim)
        z_sim[:, :] = 0.0
        for i in range(weights.shape[0]):
            e = nodes[i, :]  # extract nodes
            # evaluate future states at each node (stores in S)
            g_gu(s_sim, x_sim, e, p, S)

            # evaluate future controls at each future state
            _complete_poly_impl(S.T, deg, Phi_sim.T)
            np.dot(Phi_sim, coefs, out=X)

            # compute expectation (stores in H)
            h_gu(S, X, p, H)
            z_sim += weights[i] * H

        # get controls on the simulated points from direct_resposne
        # (stores in new_x)
        d_gu(s_sim, z_sim, p, new_x)

        # update basis matrix and do regression of new_x on s_sim to get
        # updated coefficients
        _complete_poly_impl(s_sim.T, deg, Phi_sim.T)
        new_coefs = np.ascontiguousarray(lstsq(Phi_sim, new_x)[0])

        # check whether they differ from the preceding guess
        err = (abs(new_x - x_sim).max())

        # update the series of controls and coefficients
        x_sim[:, :] = new_x
        coefs = (1 - damp) * new_coefs + damp * coefs

        if verbose:
            # update error and print if `verbose`
            err_SA = err / err_0
            err_0 = err
            t_finish = time.time()
            elapsed = t_finish - t_start
            if verbose:
                print(fmt_str.format(it, err, err_SA, elapsed))

        it += 1

    if it == maxit:
        warnings.warn(UserWarning("Maximum number of iterations reached"))

    # compute final fime and do final printout if `verbose`
    t2 = time.time()
    if verbose:
        print(stars)
        print('Elapsed: {} seconds.'.format(t2 - t1))
        print(stars)

    cp = CompletePolynomial(deg, len(s0))
    cp.fit_values(s_sim, x_sim)
    return cp