def oom_transformations(Ct, C2t, rank): # Number of states: N = Ct.shape[0] # Get the SVD of Ctau: U, s, V = scl.svd(Ct, full_matrices=False) # Reduce: s = s[:rank] U = U[:, :rank] V = V[:rank, :].transpose() # Define transformation matrices: F1 = np.dot(U, np.diag(s ** (-0.5))) F2 = np.dot(V, np.diag(s ** (-0.5))) # Compute observable operators: Xi = np.zeros((rank, N, rank)) for n in range(N): Xi[:, n, :] = np.dot(F1.T, np.dot(C2t[:, :, n], F2)) Xi_full = np.sum(Xi, axis=1) # Compute evaluator: c = np.sum(Ct, axis=1) sigma = np.dot(F1.T, c) # Compute information state: l, R = scl.eig(Xi_full.T) # Restrict eigenvalues to reasonable range: ind = np.where(np.logical_and(np.abs(l) <= (1 + 1e-2), np.real(l) >= 0.0))[0] l = l[ind] R = R[:, ind] l, R = sort_eigs(l, R) omega = np.real(R[:, 0]) omega = omega / np.dot(omega, sigma) return Xi, omega, sigma, l
def transform_C0(C, epsilon): d, V = scl.eigh(C) evmin = np.minimum(0, np.min(d)) ep = np.maximum(-evmin, epsilon) d, V = sort_eigs(d, V) ind = np.where(np.abs(d) > ep)[0] d = d[ind] V = V[:, ind] V = scale_eigenvectors(V) R = np.dot(V, np.diag(d**(-0.5))) return R
def oom_components(Ct, C2t, rank_ind=None, lcc=None, tol_one=1e-2): """ Compute OOM components and eigenvalues from count matrices: Parameters ---------- Ct : ndarray(N, N) count matrix from data C2t : sparse csc-matrix (N*N, N) two-step count matrix from data for all states, columns enumerate intermediate steps. rank_ind : ndarray(N, dtype=bool), optional, default=None indicates which singular values are accepted. By default, all non- zero singular values are accepted. lcc : ndarray(N,), optional, default=None largest connected set of the count-matrix. Two step count matrix will be reduced to this set. tol_one : float, optional, default=1e-2 keep eigenvalues of absolute value less or equal 1+tol_one. Returns ------- Xi : ndarray(M, N, M) matrix of set-observable operators oom_information_state_vector: ndarray(M,) information state vector of OOM oom_evaluator : ndarray(M,) evaluator of OOM l : ndarray(M,) eigenvalues from OOM """ import deeptime.markov.tools.estimation as me # Decompose count matrix by SVD: if lcc is not None: Ct_svd = me.largest_connected_submatrix(Ct, lcc=lcc) N1 = Ct.shape[0] else: Ct_svd = Ct V, s, W = scl.svd(Ct_svd, full_matrices=False) # Make rank decision: if rank_ind is None: ind = (s >= np.finfo(float).eps) V = V[:, rank_ind] s = s[rank_ind] W = W[rank_ind, :].T # Compute transformations: F1 = np.dot(V, np.diag(s**-0.5)) F2 = np.dot(W, np.diag(s**-0.5)) # Apply the transformations to C2t: N = Ct_svd.shape[0] M = F1.shape[1] Xi = np.zeros((M, N, M)) for n in range(N): if lcc is not None: C2t_n = C2t[:, lcc[n]] C2t_n = _reshape_sparse(C2t_n, (N1, N1)) C2t_n = me.largest_connected_submatrix(C2t_n, lcc=lcc) else: C2t_n = C2t[:, n] C2t_n = _reshape_sparse(C2t_n, (N, N)) Xi[:, n, :] = np.dot(F1.T, C2t_n.dot(F2)) # Compute sigma: c = np.sum(Ct_svd, axis=1) sigma = np.dot(F1.T, c) # Compute eigenvalues: Xi_S = np.sum(Xi, axis=1) eigenvalues, right_eigenvectors = scl.eig(Xi_S.T) # Restrict eigenvalues to reasonable range: ind = np.where( np.logical_and( np.abs(eigenvalues) <= (1 + tol_one), np.real(eigenvalues) >= 0.0))[0] eigenvalues = eigenvalues[ind] right_eigenvectors = right_eigenvectors[:, ind] # Sort and extract omega eigenvalues, right_eigenvectors = sort_eigs(eigenvalues, right_eigenvectors) omega = np.real(right_eigenvectors[:, 0]) omega = omega / np.dot(omega, sigma) return Xi, omega, sigma, eigenvalues
def setUpClass(cls): # Basis set definition: cls.nf = 10 cls.chi = np.zeros((20, cls.nf), dtype=float) for n in range(cls.nf): cls.chi[2 * n:2 * (n + 1), n] = 1.0 # Load simulations: f = np.load( pkg_resources.resource_filename(__name__, "data/test_data_koopman.npz")) trajs = [f[key] for key in f.keys()] cls.data = [cls.chi[traj, :] for traj in trajs] # Lag time: cls.tau = 10 # Truncation for small eigenvalues: cls.epsilon = 1e-6 # Compute the means: cls.mean_x = np.zeros(cls.nf) cls.mean_y = np.zeros(cls.nf) cls.frames = 0 for traj in cls.data: cls.mean_x += np.sum(traj[:-cls.tau, :], axis=0) cls.mean_y += np.sum(traj[cls.tau:, :], axis=0) cls.frames += traj[:-cls.tau, :].shape[0] cls.mean_x *= (1.0 / cls.frames) cls.mean_y *= (1.0 / cls.frames) cls.mean_rev = 0.5 * (cls.mean_x + cls.mean_y) # Compute correlations: cls.C0 = np.zeros((cls.nf, cls.nf)) cls.Ct = np.zeros((cls.nf, cls.nf)) cls.C0_rev = np.zeros((cls.nf, cls.nf)) cls.Ct_rev = np.zeros((cls.nf, cls.nf)) for traj in cls.data: itraj = (traj - cls.mean_x[None, :]).copy() cls.C0 += np.dot(itraj[:-cls.tau, :].T, itraj[:-cls.tau, :]) cls.Ct += np.dot(itraj[:-cls.tau, :].T, itraj[cls.tau:, :]) itraj = (traj - cls.mean_rev[None, :]).copy() cls.C0_rev += np.dot(itraj[:-cls.tau, :].T, itraj[:-cls.tau, :]) \ + np.dot(itraj[cls.tau:, :].T, itraj[cls.tau:, :]) cls.Ct_rev += np.dot(itraj[:-cls.tau, :].T, itraj[cls.tau:, :]) \ + np.dot(itraj[cls.tau:, :].T, itraj[:-cls.tau, :]) cls.C0 *= (1.0 / cls.frames) cls.Ct *= (1.0 / cls.frames) cls.C0_rev *= (1.0 / (2 * cls.frames)) cls.Ct_rev *= (1.0 / (2 * cls.frames)) # Compute whitening transformation: cls.R = transform_C0(cls.C0, cls.epsilon) cls.Rrev = transform_C0(cls.C0_rev, cls.epsilon) # Perform non-reversible diagonalization cls.ln, cls.Rn = scl.eig(np.dot(cls.R.T, np.dot(cls.Ct, cls.R))) cls.ln, cls.Rn = sort_eigs(cls.ln, cls.Rn) cls.Rn = np.dot(cls.R, cls.Rn) cls.Rn = scale_eigenvectors(cls.Rn) cls.tsn = -cls.tau / np.log(np.abs(cls.ln)) cls.ls, cls.Rs = scl.eig( np.dot(cls.Rrev.T, np.dot(cls.Ct_rev, cls.Rrev))) cls.ls, cls.Rs = sort_eigs(cls.ls, cls.Rs) cls.Rs = np.dot(cls.Rrev, cls.Rs) cls.Rs = scale_eigenvectors(cls.Rs) cls.tss = -cls.tau / np.log(np.abs(cls.ls)) # Compute non-reversible Koopman matrix: cls.K = np.dot(cls.R.T, np.dot(cls.Ct, cls.R)) cls.K = np.vstack((cls.K, np.dot((cls.mean_y - cls.mean_x), cls.R))) cls.K = np.hstack( (cls.K, np.eye(cls.K.shape[0], 1, k=-cls.K.shape[0] + 1))) cls.N1 = cls.K.shape[0] # Compute u-vector: ln, Un = scl.eig(cls.K.T) ln, Un = sort_eigs(ln, Un) cls.u = np.real(Un[:, 0]) v = np.eye(cls.N1, 1, k=-cls.N1 + 1)[:, 0] cls.u *= (1.0 / np.dot(cls.u, v)) # Prepare weight object: u_mod = cls.u.copy() N = cls.R.shape[0] u_input = np.zeros(N + 1) u_input[0:N] = cls.R.dot(u_mod[0:-1]) # in input basis u_input[N] = u_mod[-1] - cls.mean_x.dot(cls.R.dot(u_mod[0:-1])) cls.weight_obj = KoopmanWeightingModel(u=u_input[:-1], u_const=u_input[-1], koopman_operator=cls.K, whitening_transformation=cls.R, covariances=None) # Compute weights over all data points: cls.wtraj = [] for traj in cls.data: traj = np.dot((traj - cls.mean_x[None, :]), cls.R).copy() traj = np.hstack((traj, np.ones((traj.shape[0], 1)))) cls.wtraj.append(np.dot(traj, cls.u)) # Compute equilibrium mean: cls.mean_eq = np.zeros(cls.nf) q = 0 for traj in cls.data: qwtraj = cls.wtraj[q] cls.mean_eq += np.sum((qwtraj[:-cls.tau, None] * traj[:-cls.tau, :]), axis=0) \ + np.sum((qwtraj[:-cls.tau, None] * traj[cls.tau:, :]), axis=0) q += 1 cls.mean_eq *= (1.0 / (2 * cls.frames)) # Compute reversible C0, Ct: cls.C0_eq = np.zeros((cls.N1, cls.N1)) cls.Ct_eq = np.zeros((cls.N1, cls.N1)) q = 0 for traj in cls.data: qwtraj = cls.wtraj[q] traj = (traj - cls.mean_eq[None, :]).copy() cls.C0_eq += np.dot((qwtraj[:-cls.tau, None] * traj[:-cls.tau, :]).T, traj[:-cls.tau, :]) \ + np.dot((qwtraj[:-cls.tau, None] * traj[cls.tau:, :]).T, traj[cls.tau:, :]) cls.Ct_eq += np.dot((qwtraj[:-cls.tau, None] * traj[:-cls.tau, :]).T, traj[cls.tau:, :]) \ + np.dot((qwtraj[:-cls.tau, None] * traj[cls.tau:, :]).T, traj[:-cls.tau, :]) q += 1 cls.C0_eq *= (1.0 / (2 * cls.frames)) cls.Ct_eq *= (1.0 / (2 * cls.frames)) # Solve re-weighted eigenvalue problem: S = transform_C0(cls.C0_eq, cls.epsilon) Ct_S = np.dot(S.T, np.dot(cls.Ct_eq, S)) # Compute its eigenvalues: cls.lr, cls.Rr = scl.eigh(Ct_S) cls.lr, cls.Rr = sort_eigs(cls.lr, cls.Rr) cls.Rr = np.dot(S, cls.Rr) cls.Rr = scale_eigenvectors(cls.Rr) cls.tsr = -cls.tau / np.log(np.abs(cls.lr)) def tica(data, lag, weights=None, **params): from deeptime.decomposition import TICA return TICA(var_cutoff=0.95, lagtime=lag, **params).fit_from_timeseries( data, weights=weights).fetch_model() # Set up the model: cls.koop_rev = tica(cls.data, lag=cls.tau, scaling=None) cls.koop_eq = tica(cls.data, lag=cls.tau, weights=cls.weight_obj, scaling=None)