!pip install quantecon import numpy as np from quantecon import LQ from scipy.linalg import schur # Model parameters r = 0.05 c_bar = 2 μ = 1 # Formulate as an LQ problem Q = np.array([[1]]) R = np.zeros((2, 2)) A = [[1 + r, -c_bar + μ], [0, 1]] B = [[-1], [0]] # Construct an LQ instance lq = LQ(Q, R, A, B) def construct_LNM(A, B, Q, R): n, k = lq.n, lq.k # construct L and N L = np.zeros((2*n, 2*n)) L[:n, :n] = np.eye(n) L[:n, n:] = B @ np.linalg.inv(Q) @ B.T L[n:, n:] = A.T N = np.zeros((2*n, 2*n)) N[:n, :n] = A N[n:, :n] = -R N[n:, n:] = np.eye(n) # compute M M = np.linalg.inv(L) @ N return L, N, M L, N, M = construct_LNM(lq.A, lq.B, lq.Q, lq.R) M n = lq.n J = np.zeros((2*n, 2*n)) J[n:, :n] = np.eye(n) J[:n, n:] = -np.eye(n) M @ J @ M.T - J eigvals = sorted(np.linalg.eigvals(M)) eigvals stable_eigvals = eigvals[:n] def sort_fun(x): "Sort the eigenvalues with modules smaller than 1 to the top-left." if x in stable_eigvals: stable_eigvals.pop(stable_eigvals.index(x)) return True else: return False W, V, _ = schur(M, sort=sort_fun) W V # W11 np.diag(W[:n, :n]) # W22 np.diag(W[n:, n:]) def stable_solution(M, verbose=True): """ Given a system of linear difference equations y' = |a b| y x' = |c d| x which is potentially unstable, find the solution by imposing stability. Parameter --------- M : np.ndarray(float) The matrix represents the linear difference equations system. """ n = M.shape[0] // 2 stable_eigvals = list(sorted(np.linalg.eigvals(M))[:n]) def sort_fun(x): "Sort the eigenvalues with modules smaller than 1 to the top-left." if x in stable_eigvals: stable_eigvals.pop(stable_eigvals.index(x)) return True else: return False W, V, _ = schur(M, sort=sort_fun) if verbose: print('eigenvalues:\n') print(' W11: {}'.format(np.diag(W[:n, :n]))) print(' W22: {}'.format(np.diag(W[n:, n:]))) # compute V21 V11^{-1} P = V[n:, :n] @ np.linalg.inv(V[:n, :n]) return W, V, P def stationary_P(lq, verbose=True): """ Computes the matrix :math:`P` that represent the value function V(x) = x' P x in the infinite horizon case. Computation is via imposing stability on the solution path and using Schur decomposition. Parameters ---------- lq : qe.LQ QuantEcon class for analyzing linear quadratic optimal control problems of infinite horizon form. Returns ------- P : array_like(float) P matrix in the value function representation. """ Q = lq.Q R = lq.R A = lq.A * lq.beta ** (1/2) B = lq.B * lq.beta ** (1/2) n, k = lq.n, lq.k L, N, M = construct_LNM(A, B, Q, R) W, V, P = stable_solution(M, verbose=verbose) return P # compute P stationary_P(lq) lq.stationary_values() %%timeit stationary_P(lq, verbose=False) %%timeit lq.stationary_values() def construct_H(ρ, λ, δ): "contruct matrix H given parameters." H = np.empty((2, 2)) H[0, :] = ρ,δ H[1, :] = - (1 - λ) / λ, 1 / λ return H H = construct_H(ρ=.9, λ=.5, δ=0) W, V, P = stable_solution(H) P β = 1 / (1 + r) lq.beta = β stationary_P(lq) lq.stationary_values()