def _omega_forc_cov(self, steps): # Approximate MSE matrix \Omega(h) as defined in Lut p97 G = self._zz Ginv = L.inv(G) # memoize powers of B for speedup # TODO: see if can memoize better B = self._bmat_forc_cov() _B = {} def bpow(i): if i not in _B: _B[i] = np.linalg.matrix_power(B, i) return _B[i] phis = self.ma_rep(steps) sig_u = self.sigma_u omegas = np.zeros((steps, self.neqs, self.neqs)) for h in range(1, steps + 1): if h == 1: omegas[h-1] = self.df_model * self.sigma_u continue om = omegas[h-1] for i in range(h): for j in range(h): Bi = bpow(h - 1 - i) Bj = bpow(h - 1 - j) mult = np.trace(chain_dot(Bi.T, Ginv, Bj, G)) om += mult * chain_dot(phis[i], sig_u, phis[j].T) omegas[h-1] = om return omegas
def _omega_forc_cov(self, steps): # Approximate MSE matrix \Omega(h) as defined in Lut p97 G = self._zz Ginv = L.inv(G) # memoize powers of B for speedup # TODO: see if can memoize better B = self._bmat_forc_cov() _B = {} def bpow(i): if i not in _B: _B[i] = np.linalg.matrix_power(B, i) return _B[i] phis = self.ma_rep(steps) sig_u = self.sigma_u omegas = np.zeros((steps, self.neqs, self.neqs)) for h in range(1, steps + 1): if h == 1: omegas[h - 1] = self.df_model * self.sigma_u continue om = omegas[h - 1] for i in range(h): for j in range(h): Bi = bpow(h - 1 - i) Bj = bpow(h - 1 - j) mult = np.trace(chain_dot(Bi.T, Ginv, Bj, G)) om += mult * chain_dot(phis[i], sig_u, phis[j].T) omegas[h - 1] = om return omegas
def mse(self, steps): """ Compute theoretical forecast error variance matrices Parameters ---------- steps : int Number of steps ahead Notes ----- .. math:: \mathrm{MSE}(h) = \sum_{i=0}^{h-1} \Phi \Sigma_u \Phi^T Returns ------- forc_covs : ndarray (steps x neqs x neqs) """ ma_coefs = self.ma_rep(steps) k = len(self.sigma_u) forc_covs = np.zeros((steps, k, k)) prior = np.zeros((k, k)) for h in xrange(steps): # Sigma(h) = Sigma(h-1) + Phi Sig_u Phi' phi = ma_coefs[h] var = chain_dot(phi, self.sigma_u, phi.T) forc_covs[h] = prior = prior + var return forc_covs
def lr_effect_cov(self, orth=False): """ Returns ------- """ lre = self.lr_effects Finfty = np.kron(np.tile(lre.T, self.lags), lre) Ik = np.eye(self.neqs) if orth: Binf = np.dot(np.kron(self.P.T, np.eye(self.neqs)), Finfty) Binfbar = np.dot(np.kron(Ik, lre), self.H) return chain_dot(Binf, self.cov_a, Binf.T) + chain_dot(Binfbar, self.cov_sig, Binfbar.T) else: return chain_dot(Finfty, self.cov_a, Finfty.T)
def cum_effect_cov(self, orth=False): """ Compute asymptotic standard errors for cumulative impulse response coefficients Parameters ---------- orth : boolean Notes ----- eq. 3.7.7 (non-orth), 3.7.10 (orth) Returns ------- """ Ik = np.eye(self.neqs) PIk = np.kron(self.P.T, Ik) F = 0. covs = self._empty_covm(self.periods + 1) for i in range(self.periods + 1): if i > 0: F = F + self.G[i - 1] if orth: if i == 0: apiece = 0 else: Bn = np.dot(PIk, F) apiece = chain_dot(Bn, self.cov_a, Bn.T) Bnbar = np.dot(np.kron(Ik, self.cum_effects[i]), self.H) bpiece = chain_dot(Bnbar, self.cov_sig, Bnbar.T) / self.T covs[i] = apiece + bpiece else: if i == 0: covs[i] = np.zeros((self.neqs**2, self.neqs**2)) continue covs[i] = chain_dot(F, self.cov_a, F.T) return covs
def cum_effect_cov(self, orth=False): """ Compute asymptotic standard errors for cumulative impulse response coefficients Parameters ---------- orth : boolean Notes ----- eq. 3.7.7 (non-orth), 3.7.10 (orth) Returns ------- """ Ik = np.eye(self.neqs) PIk = np.kron(self.P.T, Ik) F = 0.0 covs = self._empty_covm(self.periods + 1) for i in range(self.periods + 1): if i > 0: F = F + self.G[i - 1] if orth: if i == 0: apiece = 0 else: Bn = np.dot(PIk, F) apiece = chain_dot(Bn, self.cov_a, Bn.T) Bnbar = np.dot(np.kron(Ik, self.cum_effects[i]), self.H) bpiece = chain_dot(Bnbar, self.cov_sig, Bnbar.T) / self.T covs[i] = apiece + bpiece else: if i == 0: covs[i] = np.zeros((self.neqs ** 2, self.neqs ** 2)) continue covs[i] = chain_dot(F, self.cov_a, F.T) return covs
def lr_effect_cov(self, orth=False): """ Returns ------- """ lre = self.lr_effects Finfty = np.kron(np.tile(lre.T, self.lags), lre) Ik = np.eye(self.neqs) if orth: Binf = np.dot(np.kron(self.P.T, np.eye(self.neqs)), Finfty) Binfbar = np.dot(np.kron(Ik, lre), self.H) return (chain_dot(Binf, self.cov_a, Binf.T) + chain_dot(Binfbar, self.cov_sig, Binfbar.T)) else: return chain_dot(Finfty, self.cov_a, Finfty.T)
def _cov_sigma(self): """ Estimated covariance matrix of vech(sigma_u) """ D_K = tsa.duplication_matrix(self.neqs) D_Kinv = npl.pinv(D_K) sigxsig = np.kron(self.sigma_u, self.sigma_u) return 2 * chain_dot(D_Kinv, sigxsig, D_Kinv.T)
def _orth_cov(self): # Lutkepohl 3.7.8 Ik = np.eye(self.neqs) PIk = np.kron(self.P.T, Ik) H = self.H covs = self._empty_covm(self.periods + 1) for i in range(self.periods + 1): if i == 0: apiece = 0 else: Ci = np.dot(PIk, self.G[i-1]) apiece = chain_dot(Ci, self.cov_a, Ci.T) Cibar = np.dot(np.kron(Ik, self.irfs[i]), H) bpiece = chain_dot(Cibar, self.cov_sig, Cibar.T) / self.T # Lutkepohl typo, cov_sig correct covs[i] = apiece + bpiece return covs
def _orth_cov(self): # Lutkepohl 3.7.8 Ik = np.eye(self.neqs) PIk = np.kron(self.P.T, Ik) H = self.H covs = self._empty_covm(self.periods + 1) for i in range(self.periods + 1): if i == 0: apiece = 0 else: Ci = np.dot(PIk, self.G[i - 1]) apiece = chain_dot(Ci, self.cov_a, Ci.T) Cibar = np.dot(np.kron(Ik, self.irfs[i]), H) bpiece = chain_dot(Cibar, self.cov_sig, Cibar.T) / self.T # Lutkepohl typo, cov_sig correct covs[i] = apiece + bpiece return covs
def H(self): k = self.neqs Lk = tsa.elimination_matrix(k) Kkk = tsa.commutation_matrix(k, k) Ik = np.eye(k) # B = chain_dot(Lk, np.eye(k**2) + commutation_matrix(k, k), # np.kron(self.P, np.eye(k)), Lk.T) # return np.dot(Lk.T, L.inv(B)) B = chain_dot(Lk, np.dot(np.kron(Ik, self.P), Kkk) + np.kron(self.P, Ik), Lk.T) return np.dot(Lk.T, L.inv(B))
def cov_ybar(self): r"""Asymptotically consistent estimate of covariance of the sample mean .. math:: \sqrt(T) (\bar{y} - \mu) \rightarrow {\cal N}(0, \Sigma_{\bar{y}})\\ \Sigma_{\bar{y}} = B \Sigma_u B^\prime, \text{where } B = (I_K - A_1 - \cdots - A_p)^{-1} Notes ----- Lutkepohl Proposition 3.3 """ Ainv = L.inv(np.eye(self.neqs) - self.coefs.sum(0)) return chain_dot(Ainv, self.sigma_u, Ainv.T)
def cov(self, orth=False): """ Compute asymptotic standard errors for impulse response coefficients Notes ----- Lutkepohl eq 3.7.5 Returns ------- """ if orth: return self._orth_cov() covs = self._empty_covm(self.periods + 1) covs[0] = np.zeros((self.neqs ** 2, self.neqs ** 2)) for i in range(1, self.periods + 1): Gi = self.G[i - 1] covs[i] = chain_dot(Gi, self.cov_a, Gi.T) return covs
def forecast_cov(ma_coefs, sig_u, steps): """ Compute theoretical forecast error variance matrices Parameters ---------- Returns ------- forc_covs : ndarray (steps x neqs x neqs) """ k = len(sig_u) forc_covs = np.zeros((steps, k, k)) prior = np.zeros((k, k)) for h in xrange(steps): # Sigma(h) = Sigma(h-1) + Phi Sig_u Phi' phi = ma_coefs[h] var = chain_dot(phi, sig_u, phi.T) forc_covs[h] = prior = prior + var return forc_covs
def test_causality(self, equation, variables, kind='f', signif=0.05, verbose=True): """Compute test statistic for null hypothesis of Granger-noncausality, general function to test joint Granger-causality of multiple variables Parameters ---------- equation : string or int Equation to test for causality variables : sequence (of strings or ints) List, tuple, etc. of variables to test for Granger-causality kind : {'f', 'wald'} Perform F-test or Wald (chi-sq) test signif : float, default 5% Significance level for computing critical values for test, defaulting to standard 0.95 level Notes ----- Null hypothesis is that there is no Granger-causality for the indicated variables. The degrees of freedom in the F-test are based on the number of variables in the VAR system, that is, degrees of freedom are equal to the number of equations in the VAR times degree of freedom of a single equation. Returns ------- results : dict """ if isinstance(variables, (basestring, int, np.integer)): variables = [variables] k, p = self.neqs, self.k_ar # number of restrictions N = len(variables) * self.k_ar # Make restriction matrix C = np.zeros((N, k**2 * p + k), dtype=float) eq_index = self.get_eq_index(equation) vinds = mat([self.get_eq_index(v) for v in variables]) # remember, vec is column order! offsets = np.concatenate( [k + k**2 * j + k * vinds + eq_index for j in range(p)]) C[np.arange(N), offsets] = 1 # Lutkepohl 3.6.5 Cb = np.dot(C, vec(self.params.T)) middle = L.inv(chain_dot(C, self.cov_params, C.T)) # wald statistic lam_wald = statistic = chain_dot(Cb, middle, Cb) if kind.lower() == 'wald': df = N dist = stats.chi2(df) elif kind.lower() == 'f': statistic = lam_wald / N df = (N, k * self.df_resid) dist = stats.f(*df) else: raise Exception('kind %s not recognized' % kind) pvalue = dist.sf(statistic) crit_value = dist.ppf(1 - signif) conclusion = 'fail to reject' if statistic < crit_value else 'reject' results = { 'statistic': statistic, 'crit_value': crit_value, 'pvalue': pvalue, 'df': df, 'conclusion': conclusion, 'signif': signif } if verbose: summ = output.causality_summary(results, variables, equation, kind) print summ return results
def kalmanfilter(F, A, H, Q, R, y, X, xi10, ntrain, history=False): """ Returns the negative log-likelihood of y conditional on the information set Assumes that the initial state and all innovations are multivariate Gaussian. Parameters ----------- F : array-like The (r x r) array holding the transition matrix for the hidden state. A : array-like The (nobs x k) array relating the predetermined variables to the observed data. H : array-like The (nobs x r) array relating the hidden state vector to the observed data. Q : array-like (r x r) variance/covariance matrix on the error term in the hidden state transition. R : array-like (nobs x nobs) variance/covariance of the noise in the observation equation. y : array-like The (nobs x 1) array holding the observed data. X : array-like The (nobs x k) array holding the predetermined variables data. xi10 : array-like Is the (r x 1) initial prior on the initial state vector. ntrain : int The number of training periods for the filter. This is the number of observations that do not affect the likelihood. Returns ------- likelihood The negative of the log likelihood history or priors, history of posterior If history is True. Notes ----- No input checking is done. """ # uses log of Hamilton 13.4.1 F = np.asarray(F) H = np.atleast_2d(np.asarray(H)) n = H.shape[1] # remember that H gets transposed y = np.asarray(y) A = np.asarray(A) X = np.asarray(X) if y.ndim == 1: # note that Y is in rows for now y = y[:, None] nobs = y.shape[0] xi10 = np.atleast_2d(np.asarray(xi10)) # if xi10.ndim == 1: # xi10[:,None] if history: state_vector = [xi10] Q = np.asarray(Q) r = xi10.shape[0] # Eq. 12.2.21, other version says P0 = Q # p10 = np.dot(np.linalg.inv(np.eye(r**2)-np.kron(F,F)),Q.ravel('F')) # p10 = np.reshape(P0, (r,r), order='F') # Assume a fixed, known intial point and set P0 = Q #TODO: this looks *slightly * different than Durbin-Koopman exact likelihood # initialization p 112 unless I've misunderstood the notational translation. p10 = Q loglikelihood = 0 for i in range(nobs): HTPHR = np.atleast_1d(np.squeeze(chain_dot(H.T, p10, H) + R)) # print HTPHR # print HTPHR.ndim # print HTPHR.shape if HTPHR.ndim == 1: HTPHRinv = 1. / HTPHR else: HTPHRinv = np.linalg.inv(HTPHR) # correct # print A.T # print X # print H.T # print xi10 # print y[i] part1 = y[i] - np.dot(A.T, X) - np.dot(H.T, xi10) # correct if i >= ntrain: # zero-index, but ntrain isn't HTPHRdet = np.linalg.det(np.atleast_2d(HTPHR)) # correct part2 = -.5 * chain_dot(part1.T, HTPHRinv, part1) # correct #TODO: Need to test with ill-conditioned problem. loglike_interm = (-n/2.) * np.log(2*np.pi) - .5*\ np.log(HTPHRdet) + part2 loglikelihood += loglike_interm # 13.2.15 Update current state xi_t based on y xi11 = xi10 + chain_dot(p10, H, HTPHRinv, part1) # 13.2.16 MSE of that state p11 = p10 - chain_dot(p10, H, HTPHRinv, H.T, p10) # 13.2.17 Update forecast about xi_{t+1} based on our F xi10 = np.dot(F, xi11) if history: state_vector.append(xi10) # 13.2.21 Update the MSE of the forecast p10 = chain_dot(F, p11, F.T) + Q if not history: return -loglikelihood else: return -loglikelihood, np.asarray(state_vector[:-1])
def test_chain_dot(): A = np.arange(1,13).reshape(3,4) B = np.arange(3,15).reshape(4,3) C = np.arange(5,8).reshape(3,1) assert_equal(tools.chain_dot(A,B,C), np.array([[1820],[4300],[6780]]))
def test_chain_dot(): A = np.arange(1, 13).reshape(3, 4) B = np.arange(3, 15).reshape(4, 3) C = np.arange(5, 8).reshape(3, 1) assert_equal(tools.chain_dot(A, B, C), np.array([[1820], [4300], [6780]]))
def kalmanfilter(F, A, H, Q, R, y, X, xi10, ntrain, history=False): """ Returns the negative log-likelihood of y conditional on the information set Assumes that the initial state and all innovations are multivariate Gaussian. Parameters ----------- F : array-like The (r x r) array holding the transition matrix for the hidden state. A : array-like The (nobs x k) array relating the predetermined variables to the observed data. H : array-like The (nobs x r) array relating the hidden state vector to the observed data. Q : array-like (r x r) variance/covariance matrix on the error term in the hidden state transition. R : array-like (nobs x nobs) variance/covariance of the noise in the observation equation. y : array-like The (nobs x 1) array holding the observed data. X : array-like The (nobs x k) array holding the predetermined variables data. xi10 : array-like Is the (r x 1) initial prior on the initial state vector. ntrain : int The number of training periods for the filter. This is the number of observations that do not affect the likelihood. Returns ------- likelihood The negative of the log likelihood history or priors, history of posterior If history is True. Notes ----- No input checking is done. """ # uses log of Hamilton 13.4.1 F = np.asarray(F) H = np.atleast_2d(np.asarray(H)) n = H.shape[1] # remember that H gets transposed y = np.asarray(y) A = np.asarray(A) X = np.asarray(X) if y.ndim == 1: # note that Y is in rows for now y = y[:,None] nobs = y.shape[0] xi10 = np.atleast_2d(np.asarray(xi10)) # if xi10.ndim == 1: # xi10[:,None] if history: state_vector = [xi10] Q = np.asarray(Q) r = xi10.shape[0] # Eq. 12.2.21, other version says P0 = Q # p10 = np.dot(np.linalg.inv(np.eye(r**2)-np.kron(F,F)),Q.ravel('F')) # p10 = np.reshape(P0, (r,r), order='F') # Assume a fixed, known intial point and set P0 = Q #TODO: this looks *slightly * different than Durbin-Koopman exact likelihood # initialization p 112 unless I've misunderstood the notational translation. p10 = Q loglikelihood = 0 for i in range(nobs): HTPHR = np.atleast_1d(np.squeeze(chain_dot(H.T,p10,H)+R)) # print HTPHR # print HTPHR.ndim # print HTPHR.shape if HTPHR.ndim == 1: HTPHRinv = 1./HTPHR else: HTPHRinv = np.linalg.inv(HTPHR) # correct # print A.T # print X # print H.T # print xi10 # print y[i] part1 = y[i] - np.dot(A.T,X) - np.dot(H.T,xi10) # correct if i >= ntrain: # zero-index, but ntrain isn't HTPHRdet = np.linalg.det(np.atleast_2d(HTPHR)) # correct part2 = -.5*chain_dot(part1.T,HTPHRinv,part1) # correct #TODO: Need to test with ill-conditioned problem. loglike_interm = (-n/2.) * np.log(2*np.pi) - .5*\ np.log(HTPHRdet) + part2 loglikelihood += loglike_interm # 13.2.15 Update current state xi_t based on y xi11 = xi10 + chain_dot(p10, H, HTPHRinv, part1) # 13.2.16 MSE of that state p11 = p10 - chain_dot(p10, H, HTPHRinv, H.T, p10) # 13.2.17 Update forecast about xi_{t+1} based on our F xi10 = np.dot(F,xi11) if history: state_vector.append(xi10) # 13.2.21 Update the MSE of the forecast p10 = chain_dot(F,p11,F.T) + Q if not history: return -loglikelihood else: return -loglikelihood, np.asarray(state_vector[:-1])
def test_causality(self, equation, variables, kind='f', signif=0.05, verbose=True): """Compute test statistic for null hypothesis of Granger-noncausality, general function to test joint Granger-causality of multiple variables Parameters ---------- equation : string or int Equation to test for causality variables : sequence (of strings or ints) List, tuple, etc. of variables to test for Granger-causality kind : {'f', 'wald'} Perform F-test or Wald (chi-sq) test signif : float, default 5% Significance level for computing critical values for test, defaulting to standard 0.95 level Notes ----- Null hypothesis is that there is no Granger-causality for the indicated variables. The degrees of freedom in the F-test are based on the number of variables in the VAR system, that is, degrees of freedom are equal to the number of equations in the VAR times degree of freedom of a single equation. Returns ------- results : dict """ if isinstance(variables, (basestring, int, np.integer)): variables = [variables] k, p = self.neqs, self.k_ar # number of restrictions N = len(variables) * self.k_ar # Make restriction matrix C = np.zeros((N, k ** 2 * p + k), dtype=float) eq_index = self.get_eq_index(equation) vinds = mat([self.get_eq_index(v) for v in variables]) # remember, vec is column order! offsets = np.concatenate([k + k ** 2 * j + k * vinds + eq_index for j in range(p)]) C[np.arange(N), offsets] = 1 # Lutkepohl 3.6.5 Cb = np.dot(C, vec(self.params.T)) middle = L.inv(chain_dot(C, self.cov_params, C.T)) # wald statistic lam_wald = statistic = chain_dot(Cb, middle, Cb) if kind.lower() == 'wald': df = N dist = stats.chi2(df) elif kind.lower() == 'f': statistic = lam_wald / N df = (N, k * self.df_resid) dist = stats.f(*df) else: raise Exception('kind %s not recognized' % kind) pvalue = dist.sf(statistic) crit_value = dist.ppf(1 - signif) conclusion = 'fail to reject' if statistic < crit_value else 'reject' results = { 'statistic' : statistic, 'crit_value' : crit_value, 'pvalue' : pvalue, 'df' : df, 'conclusion' : conclusion, 'signif' : signif } if verbose: summ = output.causality_summary(results, variables, equation, kind) print summ return results