def transpose_square_root(sigma2, method='Riccati', d=None): """For details, see here. Parameters ---------- sigma2 : array, shape (n_,n_) method : string, optional d : array, shape (k_,n_), optional Returns ------- s : array, shape (n_,n_) """ if np.ndim(sigma2) < 2: return np.squeeze(np.sqrt(sigma2)) n_ = sigma2.shape[0] if method == 'CPCA' and d is None: method = 'PCA' # Step 1: Riccati root if method == 'Riccati': s = solve_riccati(sigma2) # Step 2: Conditional principal components elif method == 'CPCA': e_d, lambda2_d = cpca_cov(sigma2, d) s = e_d * np.sqrt(lambda2_d) # Step 3: Principal components elif method == 'PCA': e, lambda2 = pca_cov(sigma2) s = e * np.sqrt(lambda2) # Step 4: Gram-Schmidt elif method == 'Gram-Schmidt': g = gram_schmidt(sigma2) s = np.linalg.inv(g).T # Step 5: Cholesky elif method == 'Cholesky': s = np.linalg.cholesky(sigma2) return s
def fit_lfm_pcfp(x, p, sig2, k_): """For details, see here. Parameters ---------- x : array, shape (t_, n_) p : array, shape (t_,) sig2 : array, shape (t_, t_) k_ : scalar Returns ------- alpha_PC : array, shape (n_,) beta_PC : array, shape (n_, k_) gamma_PC : array, shape(n_, k_) s2_PC : array, shape(n_, n_) """ t_, n_ = x.shape # Step 1: Compute HFP-expectation and covariance of x m_x, s2_x = meancov_sp(x, p) # Step 2: Compute the Choleski root of sig2 sig = np.linalg.cholesky(sig2) # Step 3: Perform spectral decomposition s2_tmp = np.linalg.solve(sig, (s2_x.dot(np.linalg.pinv(sig)))) e, lambda2 = pca_cov(s2_tmp) # Step 4: Compute optimal loadings for PC LFM beta_PC = sig @ e[:, :k_] # Step 5: Compute factor extraction matrix for PC LFM gamma_PC = (np.linalg.solve(sig, np.eye(n_))) @ e[:, :k_] # Step 6: Compute shifting term for PC LFM alpha_PC = (np.eye(n_) - beta_PC @ gamma_PC.T) @ m_x # Step 7: Compute the covariance of residuals s2_PC = sig @ e[:, k_:n_] * lambda2[k_:n_] @ e[:, k_:n_].T @ sig.T return alpha_PC, beta_PC, gamma_PC, s2_PC
def cpca_cov(sigma2, d, old=False): """For details, see here. Parameters ---------- sigma2 : array, shape (n_,n_) d : array, shape (k_,n_) Returns ------- lambda2_d : array, shape (n_,) e_d : array, shape (n_,n_) """ n_ = sigma2.shape[0] k_ = d.shape[0] i_n = np.eye(n_) lambda2_d = np.empty((n_, 1)) e_d = np.empty((n_, n_)) # Step 0. initialize constraints m_ = n_ - k_ a_n = np.copy(d) for n in range(n_): # Step 1. orthogonal projection matrix p_n = [email protected](a_n@a_n.T)@a_n # Step 2. conditional dispersion matrix s2_n = p_n @ sigma2 @ p_n # Step 3. conditional principal directions/variances e_d[:, [n]], lambda2_d[n] = pca_cov(s2_n, 1) # Step 4. Update augmented constraints matrix if n+1 <= m_-1: a_n = np.concatenate((a_n.T, sigma2 @ e_d[:, [n]]), axis=1).T elif m_ <= n+1 <= n_-1: a_n = (sigma2 @ e_d[:, :n+1]).T return e_d, lambda2_d.squeeze()
def factor_analysis_paf(sigma2, k_=None, maxiter=100, eps=1e-2): """For details, see here. Parameters ---------- sigma2 : array, shape (n_, n_) k : int, optional maxiter : integer, optional eps : float, optional Returns ------- beta_paf : array, shape (n_, k_) for k_>1 or (n_, ) for k_=1 delta2_paf : array, shape (n_,) """ n_ = sigma2.shape[0] if k_ is None: k_ = int(n_ / 2.0) # Step 0: Initialize parameters v = np.zeros(n_) b = np.zeros((n_, k_)) for i in range(maxiter): # Step 1: Compute the first k_ eigenvectors and eigenvalues e_k, lambda2_k = pca_cov(sigma2 - np.diagflat(v), k_) # Step 2: Compute the factor loadings and adjust if necessary # Step 2a b_new = e_k @ np.diag(np.sqrt(lambda2_k)) # Step 2b idx = np.diag(b_new @ b_new.T) > np.diag(sigma2) if np.any(idx): b_new[idx, :] = np.sqrt(sigma2[idx, idx] / (b_new @ b_new.T)[idx, idx]).reshape(-1, 1) *\ b_new[idx, :] # Step 3: Update residual variances v_new = np.diag(sigma2) - np.diag(b_new @ b_new.T) # Step 4: Check convergence # relative error err = max( np.max(np.abs(v_new - v)) / max(np.max(np.abs(v)), 1e-20), np.max(np.abs(b_new - b)) / max(np.max(np.abs(b)), 1e-20)) v = v_new b = b_new if err < eps: break beta_paf = b_new delta2_paf = v return np.squeeze(beta_paf), delta2_paf
def plot_ellipse(m, s2, *, r=1, n_=1000, display_ellipse=True, plot_axes=False, plot_tang_box=False, color='k', line_width=2): """For details, see here. Parameters ---------- mu : array, shape (2,) sigma2 : array, shape (2,2) r : scalar, optional n_ : scalar, optional display_ellipse : boolean, optional plot_axes : boolean, optional plot_tang_box : boolean, optional color : char, optional line_width : scalar, optional Returns ------- x : array, shape (n_points,2) """ # Step 1: compute the circle with the radius r theta = np.arange(0, 2 * np.pi, (2*np.pi)/n_) y = [r * np.cos(theta), r * np.sin(theta)] # Step 2: spectral decomposition of s2 e, lambda2 = pca_cov(s2) Diag_lambda = np.diagflat(np.sqrt(np.maximum(lambda2, 0))) # Step 3: compute the ellipse as affine transformation of the circle # stretch x = Diag_lambda@y # rotate x = e@x # translate x = m.reshape((2, 1)) + x if display_ellipse: plt.plot(x[0], x[1], lw=line_width, color=color) plt.grid(True) # Step 4: plot the tangent box if plot_tang_box: sigvec = np.sqrt(np.diag(s2)) rect = patches.Rectangle(m-r * sigvec, 2*r * sigvec[0], 2*r*sigvec[1], fill=False, linewidth=line_width) ax = plt.gca() ax.add_patch(rect) # Step 5: plot the principal axes if plot_axes: # principal axes plt.plot([m[0]-r*np.sqrt(lambda2[0])*e[0, 0], m[0]+r*np.sqrt(lambda2[0])*e[0, 0]], [m[1]-r*np.sqrt(lambda2[0])*e[1, 0], m[1]+r*np.sqrt(lambda2[0])*e[1, 0]], color=color, lw=line_width) plt.plot([m[0]-r*np.sqrt(lambda2[1])*e[0, 1], m[0]+r*np.sqrt(lambda2[1])*e[0, 1]], [m[1]-r*np.sqrt(lambda2[1])*e[1, 1], m[1]+r*np.sqrt(lambda2[1])*e[1, 1]], color=color, lw=line_width) plt.axis('equal') return x.T
def spectrum_shrink(sigma2_in, t_): """For details, see here. Parameters ---------- sigma_in : array, shape (i_, i_) t_ : scalar Returns ------- sigma_out : array, shape (i_, i_) lambda2_out : array, shape (i_, ) k_ : scalar err : scalar y_mp : array, shape (100, ) x_mp : array, shape (100, ) dist : array """ i_ = sigma2_in.shape[0] # PCA decomposition e, lambda2 = pca_cov(sigma2_in) # Determine optimal k_ ll = 1000 dist = np.ones(i_ - 1) * np.nan for k in range(i_ - 1): lambda2_k = lambda2[k + 1:] lambda2_noise = np.mean(lambda2_k) q = t_ / len(lambda2_k) # compute M-P on a very dense grid x_tmp, mp_tmp, x_lim = marchenko_pastur(q, ll, lambda2_noise) if q > 1: x_tmp = np.r_[0, x_lim[0], x_tmp] mp_tmp = np.r_[0, mp_tmp[0], mp_tmp] l_max = np.max(lambda2_k) if l_max > x_tmp[-1]: x_tmp = np.r_[x_tmp, x_lim[1], l_max] mp_tmp = np.r_[mp_tmp, 0, 0] # compute the histogram of eigenvalues hgram, x_bin = np.histogram(lambda2_k, len(x_tmp), density=True) # interpolation interp = interp1d(x_tmp, mp_tmp, fill_value='extrapolate') mp = interp(x_bin[:-1]) dist[k] = np.mean((mp - hgram)**2) err_tmp, k_tmp = np.nanmin(dist), np.nanargmin(dist) k_ = k_tmp err = err_tmp # Isotropy lambda2_out = lambda2 lambda2_noise = np.mean(lambda2[k_ + 1:]) lambda2_out[k_ + 1:] = lambda2_noise # shrunk spectrum # Output sigma2_out = e @ np.diagflat(lambda2_out) @ e.T # compute M-P on a very dense grid x_mp, y_mp, _ = marchenko_pastur(t_ / (i_ - k_ - 1), 100, lambda2_noise) return sigma2_out, lambda2_out, k_, err, y_mp, x_mp, dist
def cointegration_fp(x, p=None, *, b_threshold=0.99): """For details, see here. Parameters ---------- x : array, shape(t_, d_) p : array, shape(t_, d_) b_threshold : scalar Returns ------- c_hat : array, shape(d_, l_) beta_hat : array, shape(l_, ) """ t_ = x.shape[0] if len(x.shape) == 1: x = x.reshape((t_, 1)) d_ = 1 else: d_ = x.shape[1] if p is None: p = np.ones(t_) / t_ if p is None: p = np.ones(t_)/t_ # Step 1: estimate HFP covariance matrix _, sigma2_hat = meancov_sp(x, p) # Step 2: find eigenvectors e_hat, _ = pca_cov(sigma2_hat) # Step 3: detect cointegration vectors c_hat = [] b_hat = [] p = p[:-1] for d in np.arange(0, d_): # Step 4: Define series y_t = e_hat[:, d] @ x.T # Step 5: fit AR(1) yt = y_t[1:].reshape((-1, 1)) ytm1 = y_t[:-1].reshape((-1, 1)) _, b, _, _ = fit_lfm_mlfp(yt, ytm1, p / np.sum(p)) if np.ndim(b) < 2: b = np.array(b).reshape(-1, 1) # Step 6: check stationarity if abs(b[0, 0]) <= b_threshold: c_hat.append(list(e_hat[:, d])) b_hat.append(b[0, 0]) # Output c_hat = np.array(c_hat).T b_hat = np.array(b_hat) # Step 7: Sort according to the AR(1) parameters beta_hat c_hat = c_hat[:, np.argsort(b_hat)] b_hat = np.sort(b_hat) return c_hat, b_hat