def test_arnoldi(self): dim = 10 A = np.arange(dim**2).reshape(dim, dim) b = np.zeros(dim, dtype=float) b[0] = 1.0 n = 3 V = arnoldi(A, b, n) # check orthogonality assert_allclose(V.T.dot(V), np.identity(n), atol=1e-12) # check subspace angles V_sub = np.zeros((dim, n), dtype=float) V_sub[:, 0] = b V_sub[:, 1] = A.dot(b) V_sub[:, 2] = A.dot(V_sub[:, 1]) angles = subspace_angles(V, V_sub) assert_allclose(angles, np.zeros(n, dtype=float), atol=1e-12) # Test without orthogonalization V = arnoldi(A, b, n, orthogonal=False) # check normals for v in V.T: assert_allclose(np.linalg.norm(v), 1.0) # check subspace angles V_sub = np.zeros((dim, n), dtype=float) V_sub[:, 0] = b V_sub[:, 1] = A.dot(b) V_sub[:, 2] = A.dot(V_sub[:, 1]) angles = subspace_angles(V, V_sub) assert_allclose(angles, np.zeros(n, dtype=float), atol=1e-12)
def test_eigendecomposition_slepsc_regression( self, test_matrix_1: np.ndarray, test_matrix_1_eigenvalues: np.ndarray, test_matrix_1_eigenvectors: np.ndarray, test_matrix_2: np.ndarray, test_matrix_2_eigenvalues: np.ndarray, test_matrix_2_eigenvectors: np.ndarray, ): eigs_1, vecs_1 = _eigs_slepc(csr_matrix(test_matrix_1), k=11) assert_allclose( test_matrix_1_eigenvalues, eigs_1, ) assert_allclose(subspace_angles(test_matrix_1_eigenvectors, vecs_1), 0.0, atol=1e-6, rtol=1e-5) eigs_2, vecs_2 = _eigs_slepc(csr_matrix(test_matrix_2), k=13) assert_allclose(test_matrix_2_eigenvalues, eigs_2) assert_allclose(subspace_angles(test_matrix_2_eigenvectors, vecs_2), 0.0, atol=1e-6, rtol=1e-5)
def test_gpcca_krylov_sparse_eq_dense_mu(self, example_matrix_mu: np.ndarray): mu = int(example_matrix_mu[2, 4]) if mu == 1000: pytest.skip("rtol=0.03359514, atol=3.73976903e+14") opt_clust = {0: 3, 10: 3, 50: 3, 100: 3, 200: 2, 500: 2, 1000: 5}[mu] P, sd = get_known_input(example_matrix_mu) g_s = GPCCA(csr_matrix(P), eta=sd, method="krylov").optimize(opt_clust) g_d = GPCCA(P, eta=sd, method="krylov").optimize(opt_clust) g_b = GPCCA(P, eta=sd, method="brandts").optimize(opt_clust) assert issparse(g_s.transition_matrix) assert not issparse(g_d.transition_matrix) assert not issparse(g_b.transition_matrix) assert_allclose(g_s.memberships.sum(1), 1.0) assert_allclose(g_d.memberships.sum(1), 1.0) assert_allclose(g_b.memberships.sum(1), 1.0) X_k, X_kd, X_b = g_s.schur_vectors, g_d.schur_vectors, g_b.schur_vectors RR_k, RR_kd, RR_b = g_s.schur_matrix, g_d.schur_matrix, g_b.schur_matrix # check if it's a correct Schur form _assert_schur(P, X_k, RR_k, N=None) _assert_schur(P, X_kd, RR_kd, N=None) _assert_schur(P, X_b, RR_b, N=None) # check if they span the same subspace assert np.max(subspace_angles(X_k, X_kd)) < eps assert np.max(subspace_angles(X_kd, X_b)) < eps ms, md, mb = g_s.memberships, g_d.memberships, g_b.memberships cs, cd, cb = ( g_s.coarse_grained_transition_matrix, g_d.coarse_grained_transition_matrix, g_b.coarse_grained_transition_matrix, ) for left, right in combinations( ["brandts", "dense_krylov", "sparse_krylov"], r=2): ml, cl = locals()[f"m{left[0]}"], locals()[f"c{left[0]}"] mr, cr = locals()[f"m{right[0]}"], locals()[f"c{right[0]}"] perm = _find_permutation(ml, mr) mr = mr[:, perm] assert_allclose(mr, ml, atol=1e-4) cr = cr[perm, :][:, perm] try: assert_allclose(cr, cl, atol=1e-4) except AssertionError as e: raise RuntimeError(f"Comparing: {left} and {right}.") from e
def test_gpcca_krylov_sparse_eq_dense_count(self, P: np.ndarray, sd: np.ndarray): # all of them cluster optimally into 3 clusters g_s = GPCCA(csr_matrix(P), eta=sd, method="krylov").optimize([2, 5]) g_d = GPCCA(P, eta=sd, method="krylov").optimize([2, 5]) g_b = GPCCA(P, eta=sd, method="brandts").optimize([2, 5]) assert issparse(g_s.transition_matrix) assert not issparse(g_d.transition_matrix) assert not issparse(g_b.transition_matrix) assert_allclose(g_s.memberships.sum(1), 1.0) assert_allclose(g_d.memberships.sum(1), 1.0) assert_allclose(g_b.memberships.sum(1), 1.0) X_k, X_kd, X_b = g_s.schur_vectors, g_d.schur_vectors, g_b.schur_vectors RR_k, RR_kd, RR_b = g_s.schur_matrix, g_d.schur_matrix, g_b.schur_matrix # check if it's a correct Schur form _assert_schur(P, X_k, RR_k, N=None, subspace=True) _assert_schur(P, X_kd, RR_kd, N=None, subspace=True) _assert_schur(P, X_b, RR_b, N=None, subspace=True) # check if they span the same subspace assert np.max(subspace_angles(X_k, X_kd)) < eps assert np.max(subspace_angles(X_kd, X_b)) < eps ms, md, mb = g_s.memberships, g_d.memberships, g_b.memberships cs, cd, cb = ( g_s.coarse_grained_transition_matrix, g_d.coarse_grained_transition_matrix, g_b.coarse_grained_transition_matrix, ) for left, right in combinations( ["brandts", "dense_krylov", "sparse_krylov"], r=2): ml, cl = locals()[f"m{left[0]}"], locals()[f"c{left[0]}"] mr, cr = locals()[f"m{right[0]}"], locals()[f"c{right[0]}"] perm = _find_permutation(ml, mr) mr = mr[:, perm] assert_allclose(mr, ml) cr = cr[perm, :][:, perm] try: assert_allclose(cr, cl) except AssertionError as e: raise RuntimeError(f"Comparing: {left} and {right}.") from e
def compute_subspace_angles(): """Computes average subspace angle between each pair of eigendistortions""" img_name = 'dog' jacobians = load_eigendistortions(img_name) combos = [comb for comb in combinations(range(20), 2)] combos = combos + [(i, i) for i in range(20)] all_angles = {} n_boot = 20 save_dir = op.join(DIR_DATA, 'subspace_angles') filename = 'subspace_angles_' + img_name safe_mkdir(save_dir) if op.exists(op.join(save_dir, filename)): return for comb in tqdm(combos): tmp = [] for _ in tqdm(range(n_boot)): ind1, ind2 = comb j1 = jacobians[torch.randint(0, 20, (ind1+1, ))] j2 = jacobians[torch.randint(0, 20, (ind2+1, ))] _, _, v1 = torch.svd(j1.mean(0)) _, _, v2 = torch.svd(j2.mean(0)) tmp.append(subspace_angles(v1, v2)) all_angles.update({str(comb): tmp.copy()}) with open(op.join(save_dir, filename + '.pkl'), 'wb') as f: pickle.dump(all_angles, f, pickle.HIGHEST_PROTOCOL) return all_angles
def test_sweep(m=5): dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) # Default arguments X, y = dom.sweep() assert np.all(dom.isinside(X)) # Specify sample x = dom.sample() X, y = dom.sweep(x=x) assert np.all(dom.isinside(X)) # Check x is on the line dom2 = psdr.ConvexHullDomain(X) assert dom2.isinside(x) # Specify direction p = np.random.randn(m) X, y = dom.sweep(p=p) assert np.all(dom.isinside(X)) d = (X[-1] - X[0]).reshape(-1, 1) assert np.isclose(subspace_angles(d, p.reshape(-1, 1)), 0) # Check corner X, y = dom.sweep(p=p, corner=True) c = dom.corner(p) assert np.any([np.isclose(x, c) for x in X])
def test_lipschitz_approx_class(): r""" Integration testing """ fun = psdr.demos.OTLCircuit() X = fun.domain.sample_grid(2) lip = psdr.LipschitzMatrix() lip.fit(grads=fun.grad(X)) lipapprox = LipschitzApproximation(lip.L, fun.domain) X = fun.domain.sample(40) fX = fun(X) #fX += 0.1*np.random.randn(*fX.shape) lipapprox.fit(X, fX[:, 0]) y = lipapprox(X) print(fX[:, 0] - y) err = np.max(np.abs(fX[:, 0] - y)) assert err < 1e-9 # Check identification of active subspace for i in range(1, len(fun.domain)): U = lip.U[:, :-i] LUU = lip.L @ U @ U.T lipapprox = LipschitzApproximation(LUU, fun.domain) print(U.shape) print(lipapprox.U.shape) ang = subspace_angles(U, lipapprox.U) print(ang) assert np.max(ang) < 1e-7, "Did not correctly identify active subspace"
def tran_angles(df, df2): r"""Subspace angles Compute the subspace angles between two matrices. A wrapper for scipy.linalg.subspace_angles that corrects for column ordering. Row ordering is assumed. Args: df (DataFrame): First matrix to compare df2 (DataFrame): Second matrix to compare Returns: array: Array of angles (in radians) Examples: >>> import grama as gr >>> import pandas as pd >>> df = pd.DataFrame(dict(v=[+1, +1])) >>> df_v1 = pd.DataFrame(dict(w=[+1, -1])) >>> df_v2 = pd.DataFrame(dict(w=[+1, +1])) >>> theta1 = angles(df, df_v1) >>> theta2 = angles(df, df_v2) """ ## Compute subspace angles A1 = df.values A2 = df2.values return subspace_angles(A1, A2)
def test_P_i(self, P_i: np.ndarray, method: str): if method == "krylov": pytest.importorskip("mpi4py") pytest.importorskip("petsc4py") pytest.importorskip("slepc4py") g = GPCCA(P_i, eta=None, method=method) for m in range(2, 8): try: g.optimize(m) except ValueError: continue X, RR = g.schur_vectors, g.schur_matrix assert_allclose(g.memberships.sum(1), 1.0) assert_allclose(g.coarse_grained_transition_matrix.sum(1), 1.0) assert_allclose(g.coarse_grained_input_distribution.sum(), 1.0) if g.coarse_grained_stationary_probability is not None: assert_allclose(g.coarse_grained_stationary_probability.sum(), 1.0) np.testing.assert_allclose(X[:, 0], 1.0) assert np.max(subspace_angles(P_i @ X, X @ RR)) < eps
def test_proj_raw_duration(duration, sfreq): """Test equivalence of `duration` options.""" n_ch, n_dim = 30, 3 rng = np.random.RandomState(0) signals = rng.randn(n_dim, 10000) mixing = rng.randn(n_ch, n_dim) + [0, 1, 2] data = np.dot(mixing, signals) raw = RawArray(data, create_info(n_ch, sfreq, 'eeg')) raw.set_eeg_reference(projection=True) n_eff = int(round(raw.info['sfreq'] * duration)) # crop to an even "duration" number of epochs stop = ((len(raw.times) // n_eff) * n_eff - 1) / raw.info['sfreq'] raw.crop(0, stop) proj_def = compute_proj_raw(raw, n_eeg=n_dim) proj_dur = compute_proj_raw(raw, duration=duration, n_eeg=n_dim) proj_none = compute_proj_raw(raw, duration=None, n_eeg=n_dim) assert len(proj_dur) == len(proj_none) == len(proj_def) == n_dim # proj_def is not in here because it does not necessarily evenly divide # the signal length: for pu, pn in zip(proj_dur, proj_none): assert_allclose(pu['data']['data'], pn['data']['data']) # but we can test it here since it should still be a small subspace angle: for proj in (proj_dur, proj_none, proj_def): computed = np.concatenate([p['data']['data'] for p in proj], 0) angle = np.rad2deg(linalg.subspace_angles(computed.T, mixing)[0]) assert angle < 1e-5
def subspace_dist(A, B): ''' Inputs: A, B - Two subspaces (columns of each matrix are basis for the subspace) Outputs: The distance between the two subspaces, defined as the Frobenius norm of the sine of principal angles ''' return np.linalg.norm(np.sin(subspace_angles(A, B)))
def find_theta_max(p,dim): theta_max = [] for i in range(100): rand_subspac1 = orth(np.random.randn(p, dim)) rand_subspac2 = orth(np.random.randn(p, dim)) theta_max.append(subspace_angles(rand_subspac1,rand_subspac2).max()) #using min or max max_avg_theta = np.average(theta_max) return(max_avg_theta)
def find_theta_max(b, t, k): theta_max = [] for i in range(1, k + 1): for j in range(1, i): theta_max.append(subspace_angles(b[i], b[j]).max()) max_avg_theta = mean(theta_max) theta = max_avg_theta * t return theta
def find_a_for_theta(a,p=p, dim=dim,theta = theta) : temp_theta = [] for i in range(100): rand_subspac0 = orth(np.random.randn(p, dim)) rand_subspac1 = orth(np.random.randn(p, dim)) rand_subspac2 = orth(np.random.randn(p, dim)) temp_theta.append(subspace_angles(rand_subspac0*(1-a)+rand_subspac1*a,rand_subspac0*(1-a)+rand_subspac2*a).max()) return (np.average(temp_theta)-theta)
def performance_measure1(k,B1,B2): all_per = list(it.permutations(range(k))) sum_cos_angles_all_per = np.zeros(len(all_per)) for l, val in enumerate(all_per): for i in range(k) : if B2[val[i]].shape[1]>0 : # handling with empty clusters sum_cos_angles_all_per[l]+= (math.cos(subspace_angles(B1[i],B2[val[i]]).max()))**2 #use min or max???????????????? cost_subspace = sum_cos_angles_all_per.max() return (cost_subspace)
def test_pod(self): S = np.array([[1.0, 1.0, 1.0, 0.0, 0.0, 0.0], [2.0, 0.0, 0.0, 2.0, 0.0, 0.0], [2.5, 1.5, 0.0, 0.0, 0.0, 0.5], [4.0, 0.0, 0.0, 4.00000000001, 0.0, 0.0]]).T sigma, V = pod(S, 2) angles = subspace_angles(V, S) assert_allclose(angles, 0.0, atol=1e-14) self.assertEqual(V.shape[1], 2) sigma, V = pod(S, 4) angles = subspace_angles(V, S) assert_allclose(angles, 0.0, atol=1e-14) self.assertEqual(V.shape[1], 4) print(sigma) sigma, V = pod(S, tol=1e-10) self.assertEqual(V.shape[1], 3)
def measure_cost_subspace(k, B1, B2): all_perm = list(permutations(range(k))) sum_cos_angles_all_per = np.zeros(len(all_perm)) for l, perm in enumerate(all_perm): for i in range(k): if B2[perm[i]].shape[1] > 0: # handling with empty clusters sum_cos_angles_all_per[l] += math.cos( subspace_angles(B1[i], B2[perm[i]]).max()) ** 2 # use min or max???????????????? cost_subspace = sum_cos_angles_all_per.max() return cost_subspace
def test_arnoldi_simple(): """Test the Arnoldi algorithm for a simple system.""" num_directions = 3 x = np.empty((a.shape[0], num_directions)) x[:, 0] = b.squeeze() x[:, 0] /= np.linalg.norm(x[:, 0]) for i in range(1, num_directions): x[:, i] = np.linalg.solve(a, x[:, i - 1]) x[:, i] /= np.linalg.norm(x[:, i]) rks = arnoldi(a, b, num_directions) npt.assert_almost_equal(np.abs(subspace_angles(x, rks)).max(), 0.0)
def test_do_schur_krylov_eq_brandts(self, example_matrix_mu: np.ndarray): P, sd = get_known_input(example_matrix_mu) X_b, RR_b, _ = _do_schur(P, eta=sd, m=3, method="brandts") X_k, RR_k, _ = _do_schur(P, eta=sd, m=3, method="krylov") # check if it's a correct Schur form _assert_schur(P, X_b, RR_b, N=None) _assert_schur(P, X_k, RR_k, N=None) # check if they span the same subspace assert np.max(subspace_angles(X_b, X_k)) < eps
def c_subspace(data, Bs, d, n_subspaces, labels): metric = 0 for i in range(n_subspaces): b_hat = data[labels == i].T if data[labels == i].shape[0] > d: centered_data = data[labels == i] - np.mean(data[labels == i], axis=1)[:, None] pca = PCA(n_components=d).fit(centered_data.T) b_hat = pca.transform(centered_data.T) metric += np.cos(max(subspace_angles(Bs[i], b_hat)))**2 return metric
def _find_theta(subspaces): K = len(subspaces) sum_thetas = 0 for i in range(K): for j in range(i): a = subspace_angles(subspaces[i], subspaces[j])[0] if a > np.pi / 2: print(a) sum_thetas += a if (1 / binom(K, 2)) * sum_thetas > np.pi / 2: print((1 / binom(K, 2)) * sum_thetas) return (1 / binom(K, 2)) * sum_thetas
def test_arnoldi_xxl(): """Test the Arnoldi algorithm for a larger system.""" np.random.seed(777) a_xxl = np.random.rand(100, 100) b_xxl = np.random.rand(100) num_directions = 10 x = np.empty((a_xxl.shape[0], num_directions)) x[:, 0] = b_xxl x[:, 0] /= np.linalg.norm(x[:, 0]) for i in range(1, num_directions): x[:, i] = np.linalg.solve(a_xxl, x[:, i - 1]) x[:, i] /= np.linalg.norm(x[:, i]) rks = arnoldi(a_xxl, b_xxl, num_directions) npt.assert_almost_equal(np.abs(subspace_angles(x, rks)).max(), 0.0)
def plot_angles(X, y): n_pairs = 5000 same = np.array([ X[np.random.choice(np.where(y == np.random.choice(n_clusters))[0], size=2)] for _ in range(n_pairs) ]) labels = np.array([ np.random.choice(n_clusters, size=2, replace=False) for _ in range(n_pairs) ]).flatten() different = np.array([ X[np.random.choice(np.where(y == label)[0])] for label in labels ]).reshape((n_pairs, 2, 784)) same_angles = [ np.degrees(max(subspace_angles(pair[0][:, None], pair[1][:, None]))) for pair in same ] diff_angles = [ np.degrees(max(subspace_angles(pair[0][:, None], pair[1][:, None]))) for pair in different ] plt.figure(figsize=(10, 7)) plt.hist(same_angles, alpha=0.5, label="same class", bins=100, density=True) plt.hist(diff_angles, alpha=0.5, label="different classes", bins=100, density=True) plt.title("Histogram of angles between pairs of data-points") plt.legend() plt.show()
def principal_angle_mat(X, Y): """Principal angles between two subspaces X and Y. Parameters ---------- X : ndarray feature vectors in M-dimensional space, shape (Nx, M). Y : ndarray feature vectors in M-dimensional space, shape (Ny, M). Returns ------- float subspace angle in radian. """ angles = subspace_angles(X.T, Y.T) return angles[0]
def test_normal_case( self, P: np.ndarray, sd: np.ndarray, count_sd: np.ndarray, count_Pc: np.ndarray, count_chi: np.ndarray, ): assert_allclose(sd, count_sd) g = GPCCA(P, eta=sd) g.optimize((2, 10)) Pc = g.coarse_grained_transition_matrix assert_allclose(Pc, count_Pc, atol=eps) assert_allclose(Pc.sum(1), 1.0) assert_allclose(g.coarse_grained_transition_matrix.sum(1), 1.0) assert_allclose(g.memberships.sum(1), 1.0) assert np.max(subspace_angles(g.memberships, count_chi)) < eps
def _assert_schur( P: np.ndarray, X: np.ndarray, RR: np.ndarray, N: Optional[int] = None, subspace: bool = False, ): if N is not None: np.testing.assert_array_equal(P.shape, [N, N]) np.testing.assert_array_equal(X.shape, [N, N]) np.testing.assert_array_equal(RR.shape, [N, N]) if subspace: assert_allclose(subspace_angles(P @ X, X @ RR), 0.0, atol=1e-6, rtol=1e-5) else: assert np.all(np.abs(X @ RR - P @ X) < eps), np.abs(X @ RR - P @ X).max() assert np.all(np.abs(X[:, 0] - 1) < eps), np.abs(X[:, 0]).max()
def angles(df1, df2, rowname="rowname"): """Subspace angles Compute the subspace angles between two matrices. A wrapper for scipy.linalg.subspace_angles that corrects for row and column ordering. Args: df1 (DataFrame): First matrix to compare df2 (DataFrame): Second matrix to compare rowname (str): Rownames which define new column names Must be same for df1 and df2 Returns: np.array: Array of angles (in radians) Examples: from pybuck import * >>> df = col_matrix(v = dict(x=+1, y=+1)) >>> df_v1 = col_matrix(w = dict(x=+1, y=-1)) >>> df_v2 = col_matrix(w = dict(x=+1, y=+1)) >>> theta1 = angles(df, df_v1) >>> theta2 = angles(df, df_v2) """ ## Check invariants if not (rowname in df1.columns): raise ValueError("df1 must have {} column".format(rowname)) if not (rowname in df2.columns): raise ValueError("df2 must have {} column".format(rowname)) ## Compute subspace angles A1 = df1.sort_values(rowname) \ .drop(rowname, axis=1) \ .values A2 = df2.sort_values(rowname) \ .drop(rowname, axis=1) \ .values return subspace_angles(A1, A2)
def test_krylov_basis(self): dim = 10 m_toeplitz_arr = np.zeros(dim) m_toeplitz_arr[0] = 2.0 m_toeplitz_arr[1] = -1.0 M = toeplitz(m_toeplitz_arr) k_toeplitz_arr = np.zeros(dim) k_toeplitz_arr[0] = 5.0 k_toeplitz_arr[1] = -2.0 k_toeplitz_arr[1] = -0.5 K = toeplitz(k_toeplitz_arr) b = np.zeros(dim, dtype=float) b[0] = 1.0 n = 3 V = krylov_basis(M, K, b, n, mass_orth=False) # Check if K^{-1} b is in basis U = K.dot(V) angle = subspace_angles(U, b.reshape(-1, 1)) assert_allclose(angle, 0.0, atol=1e-10) # Check if K^{-1} M K^{-1} b is in basis u = solve(K, M.dot(solve(K, b))) angle = subspace_angles(u.reshape(-1, 1), V) assert_allclose(angle, 0.0, atol=1e-10) # check orthogonality and shape assert_allclose(V.T.dot(V), np.identity(n), atol=1e-12) rows, cols = V.shape self.assertEqual(rows, dim) self.assertEqual(cols, n) # check m-orthogonality and shape V = krylov_basis(M, K, b, n, mass_orth=True, n_iter_orth=2) assert_allclose(V.T.dot(M).dot(V), np.identity(n), atol=1e-12) rows, cols = V.shape self.assertEqual(rows, dim) self.assertEqual(cols, n) # check different number of moments n = 5 V = krylov_basis(M, K, b, n, mass_orth=False) assert_allclose(V.T.dot(V), np.identity(n), atol=1e-12) rows, cols = V.shape self.assertEqual(rows, dim) self.assertEqual(cols, n) # check if it also works with matrix inputs no_of_inputs = 2 B = np.zeros((dim, no_of_inputs)) B[0, 0] = 1.0 B[3, 1] = 1.0 # check orthogonality and shape V = krylov_basis(M, K, B, n, mass_orth=False) assert_allclose(V.T.dot(V), np.identity(n * no_of_inputs), atol=1e-12) rows, cols = V.shape self.assertEqual(rows, dim) self.assertEqual(cols, n * no_of_inputs) # check m-orthogonality and shape V = krylov_basis(M, K, B, n, mass_orth=True) assert_allclose(V.T.dot(M).dot(V), np.identity(n * no_of_inputs), atol=1e-12) rows, cols = V.shape self.assertEqual(rows, dim) self.assertEqual(cols, n * no_of_inputs)
def find_a_for_theta(a, b=b, k=k, theta=theta): temp_theta = [] for i in range(1, k + 1): for j in range(1, i): temp_theta.append(subspace_angles(b[0] * (1 - a) + b[i] * a, b[0] * (1 - a) + b[j] * a).max()) return mean(temp_theta) - theta
def _check_schur(P: np.ndarray, Q: np.ndarray, R: np.ndarray, eigenvalues: np.ndarray, method: str) -> None: """ Run a number of checks on the sorted Schur decomposition. Parameters ---------- %(P)s Q %(Q_sort)s R %(R_sort)s eigenvalues %(eigenvalues_m)s %(method)s Returns ------- Nothing. """ m = len(eigenvalues) # check the dimensions if Q.shape[1] != len(eigenvalues): raise ValueError( f"Number of Schur vectors does not match number of eigenvalues for `method={method!r}`." ) if R.shape[0] != R.shape[1]: raise ValueError(f"R is not rectangular for `method={method!r}`.") if P.shape[0] != Q.shape[0]: raise ValueError( f"First dimension in P does not match first dimension in Q for `method={method!r}`." ) if R.shape[0] != Q.shape[1]: raise ValueError( f"First dimension in R does not match second dimension in Q for `method={method!r}`." ) # check whether things are real if not np.all(np.isreal(Q)): raise TypeError( f"The orthonormal basis of the subspace returned by `method={method!r}` is not real. " "G-PCCA needs real basis vectors to work.") dummy = np.dot(P, csr_matrix(Q) if issparse(P) else Q) if issparse(dummy): dummy = dummy.toarray() dummy1 = np.dot(Q, np.diag(eigenvalues)) # dummy2 = np.concatenate((dummy, dummy1), axis=1) dummy3 = subspace_angles(dummy, dummy1) # test1 = ( ( matrix_rank(dummy2) - matrix_rank(dummy) ) == 0 ) test2 = np.allclose(dummy3, 0.0, atol=1e-8, rtol=1e-5) test3 = dummy3.shape[0] == m dummy4 = subspace_angles(dummy, Q) test4 = np.allclose(dummy4, 0.0, atol=1e-6, rtol=1e-5) if not test4: raise ValueError( f"According to `scipy.linalg.subspace_angles()`, `{method}` didn't " f"return an invariant subspace of P. The subspace angles are: `{dummy4}`." ) if not test2: warnings.warn( f"According to `scipy.linalg.subspace_angles()`, `{method}` didn't " f"return the invariant subspace associated with the top k eigenvalues, " f"since the subspace angles between the column spaces of P*Q and Q*L " f"aren't near zero (L is a diagonal matrix with the " f"sorted top eigenvalues on the diagonal). The subspace angles are: `{dummy3}`." ) if not test3: warnings.warn( f"According to `scipy.linalg.subspace_angles()`, the dimension of the " f"column space of P*Q and/or Q*L is not equal to m (L is a diagonal " f"matrix with the sorted top eigenvalues on the diagonal), method=`{method}`." )