def update(self): # Unpack parameters A, alpha, beta, delta, gamma, rho, sigma = self._unpack_params() ns, Qn = self.ncgm.ns, self.ncgm.Qn ncgm, grid = self.ncgm, self.ncgm.grid k, z = grid[:, 0], grid[:, 1] # Evaluate RHS of EE temp = np.empty((n_complete(2, self.degree), ns)) rhs_ee = 0.0 for iz1 in range(Qn): # Build t+1 decisions zp = ncgm.z1[:, iz1] _complete_poly_impl(np.vstack([self.KP, zp]), self.degree, temp) # Compute k_{t+2} and the corresponding c_{t+1} kpp = self.k_coeffs @ temp cp = expendables_t(self.KP, zp, A, alpha, delta) - kpp foo = beta * ncgm.weights[iz1] * (1.0 - delta + df(self.KP, zp, A, alpha)) foo *= du(cp, gamma) rhs_ee += foo # Determine c_t using euler equation c = duinv(rhs_ee, gamma) kp = expendables_t(k, z, A, alpha, delta) - c VF = u(c, gamma) + beta * self.compute_EV(kp=kp) return kp, VF
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)
def test_complete_derivative(): # Test derivative vector z = np.array([1, 2, 3]) sol_vec = np.array([0.0, 1.0, 0.0, 0.0, 2.0, 2.0, 3.0, 0.0, 0.0, 0.0]) out_vec = np.empty(n_complete(3, 2)) _complete_poly_der_impl_vec(z, 2, 0, out_vec) assert (abs(out_vec - sol_vec).max() < 1e-10) # Test derivative matrix z = np.arange(1, 7).reshape(3, 2) out_mat = complete_polynomial_der(z, 2, 1) assert (abs(out_mat[0, :]).max() < 1e-10) assert (abs(out_mat[2, :] - np.ones(2)).max() < 1e-10) assert (abs(out_mat[-2, :] - np.array([5.0, 6.0])).max() < 1e-10)
def test_complete_derivative(): # TODO: Currently if z has a 0 value then it breaks because occasionally # tries to raise 0 to a negative power -- This can be fixed by # checking whether coefficient is 0 before trying to do anything... # Test derivative vector z = np.array([1, 2, 3]) sol_vec = np.array([0.0, 1.0, 0.0, 0.0, 2.0, 2.0, 3.0, 0.0, 0.0, 0.0]) out_vec = np.empty(n_complete(3, 2)) _complete_poly_der_impl_vec(z, 2, 0, out_vec) assert (abs(out_vec - sol_vec).max() < 1e-10) # Test derivative matrix z = np.arange(1, 7).reshape(3, 2) out_mat = complete_polynomial_der(z, 2, 1) assert (abs(out_mat[0, :]).max() < 1e-10) assert (abs(out_mat[2, :] - np.ones(2)).max() < 1e-10) assert (abs(out_mat[-2, :] - np.array([5.0, 6.0])).max() < 1e-10)
def jit_ee(params, degree, v_coeffs, nodes, weights, ks, zs): # Unpack parameters A, alpha, beta, delta, gamma, rho, sigma = param_unpack(params) # Allocate space for temporary vector to fill with complete polynomials temp = np.empty(n_complete(2, degree)) T = ks.size Qn = weights.size # Allocate space to store euler errors ee = np.empty(T) # Iterate over all ks and zs for t in range(T): # Current states k, z = ks[t], zs[t] # Compute decision for kp and implied c k1 = env_cond_kp(temp, params, degree, v_coeffs, k, z) c = expendables_t(k, z, A, alpha, delta) - k1 # Compute euler error for period t lhs = du(c, gamma) rhs = 0.0 for i in range(Qn): # Get productivity tomorrow z1 = z**rho * np.exp(nodes[i]) # Compute decision for kpp and implied c k2 = env_cond_kp(temp, params, degree, v_coeffs, k1, z1) c1 = expendables_t(k1, z1, A, alpha, delta) - k2 rhs = rhs + weights[i] * du( c1, gamma) * (1 - delta + df(k1, z1, A, alpha)) ee[t] = np.abs(1.0 - beta * rhs / lhs) return ee
def jit_simulate_ncgm(params, degree, v_coeffs, T, nburn, shocks): "Simulates economy using envelope condition as policy rule" # Unpack parameters A, alpha, beta, delta, gamma, rho, sigma = param_unpack(params) # Allocate space for output ksim = np.empty(T + nburn) zsim = np.empty(T + nburn) ksim[0], zsim[0] = 1.0, 1.0 # Allocate space for temporary vector to fill with complete polynomials temp = np.empty(n_complete(2, degree)) # Simulate for t in range(1, T + nburn): # Evaluate policy for today given yesterdays state kp = env_cond_kp(temp, params, degree, v_coeffs, ksim[t - 1], zsim[t - 1]) # Draw new z and update k using policy from above zsim[t] = zsim[t - 1]**rho * np.exp(sigma * shocks[t]) ksim[t] = kp return ksim[nburn:], zsim[nburn:]
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