def test_3state_prev(self): dtraj = np.array([0, 1, 2, 0, 3, 4]) import deeptime.markov.tools.estimation as msmest for rev in [True, False]: hmm = init.discrete.metastable_from_data(dtraj, n_hidden_states=3, lagtime=1, reversible=rev) assert msmana.is_transition_matrix( hmm.transition_model.transition_matrix) if rev: assert msmana.is_reversible( hmm.transition_model.transition_matrix) assert np.allclose(hmm.output_probabilities.sum(axis=1), 1) for rev in [True, False]: C = TransitionCountEstimator( lagtime=1, count_mode="sliding").fit(dtraj).fetch_model().count_matrix C += msmest.prior_neighbor(C, 0.001) hmm = init.discrete.metastable_from_data(dtraj, n_hidden_states=3, lagtime=1, reversible=rev) np.testing.assert_( msmana.is_transition_matrix( hmm.transition_model.transition_matrix)) if rev: np.testing.assert_( msmana.is_reversible( hmm.transition_model.transition_matrix)) np.testing.assert_allclose(hmm.output_probabilities.sum(axis=1), 1.)
def test_with_almost_converged_stat_dist(sparse_mode): """ test for https://github.com/markovmodel/msmtools/issues/106 """ from deeptime.markov.tools.analysis import committor, is_reversible from deeptime.markov.tools.flux import flux_matrix, to_netflux from deeptime.markov import reactive_flux, ReactiveFlux T = np.array([[ 0.2576419223095193, 0.2254214623509954, 0.248270708174756, 0.2686659071647294 ], [ 0.2233847186210225, 0.2130434781715344, 0.2793477268264001, 0.284224076381043 ], [ 0.2118717275169231, 0.2405661227681972, 0.2943396213976011, 0.2532225283172787 ], [ 0.2328617711043517, 0.2485926610067547, 0.2571819311236834, 0.2613636367652102 ]]) if sparse_mode: T = csr_matrix(T) mu = np.array([ 0.2306979668517676, 0.2328013892993006, 0.2703312416016573, 0.2661694022472743 ]) assert is_reversible(T) np.testing.assert_allclose(T.T.dot(mu).T, mu) np.testing.assert_equal(T.T.dot(mu).T, T.T.dot(mu)) A = [0] B = [1] # forward committor qplus = committor(T, A, B, forward=True, mu=mu) # backward committor if is_reversible(T, mu=mu): qminus = 1.0 - qplus else: qminus = committor(T, A, B, forward=False, mu=mu) tpt_obj = reactive_flux(T, A, B) tpt_obj.major_flux(1.0) # gross flux grossflux = flux_matrix(T, mu, qminus, qplus, netflux=False) # net flux netflux = to_netflux(grossflux) F = ReactiveFlux(A, B, netflux, stationary_distribution=mu, qminus=qminus, qplus=qplus, gross_flux=grossflux) F.pathways(1.0)
def test_transition_matrix(self): for P in [ self.hmsm_lag1.transition_matrix, self.hmsm_lag1.transition_matrix ]: assert is_transition_matrix(P) assert is_reversible(P)
def test_transition_matrix(oom_msm_scenario): for msm in oom_msm_scenario.msms: P = msm.transition_matrix # should be ndarray by default np.testing.assert_( isinstance(P, np.ndarray) or isinstance(P, scipy.sparse.csr_matrix)) # shape np.testing.assert_equal(P.shape, (msm.n_states, msm.n_states)) # test transition matrix properties import deeptime.markov.tools.analysis as msmana np.testing.assert_(msmana.is_transition_matrix(P)) np.testing.assert_(msmana.is_connected(P)) # REVERSIBLE if msm.reversible: np.testing.assert_(msmana.is_reversible(P)) # Test equality with model: from scipy.sparse import issparse if issparse(P): P = P.toarray() if msm.reversible: np.testing.assert_allclose( P, oom_msm_scenario.rmsmrev.transition_matrix) else: np.testing.assert_allclose(P, oom_msm_scenario.rmsm.transition_matrix)
def _transition_matrix_samples(self, msm): Psamples = msm.sample_f('transition_matrix') # shape assert np.array_equal(np.shape(Psamples), (self.nsamples, self.nstates, self.nstates)) # consistency for P in Psamples: assert is_transition_matrix(P) assert is_reversible(P)
def test_transition_matrix(self): import deeptime.markov.tools.analysis as msmana for P in [ self.hmm_lag1.transition_model.transition_matrix, self.hmm_lag10.transition_model.transition_matrix ]: np.testing.assert_(msmana.is_transition_matrix(P)) np.testing.assert_(msmana.is_reversible(P))
def test_transition_matrix_samples(self): Psamples = np.array([m.transition_model.transition_matrix for m in self.bhmm]) # shape assert np.array_equal(np.shape(Psamples), (self.n_samples, self.n_states, self.n_states)) # consistency import deeptime.markov.tools.analysis as msmana for P in Psamples: assert msmana.is_transition_matrix(P) assert msmana.is_reversible(P)
def test_rdl_decomposition(self): P = self.bdc.transition_matrix assert is_reversible(P) mu = self.bdc.stationary_distribution """Non-reversible""" """k=None""" Rn, Dn, Ln = rdl_decomposition(P) Xn = np.dot(Ln, Rn) """Right-eigenvectors""" assert_allclose(np.dot(P, Rn), np.dot(Rn, Dn)) """Left-eigenvectors""" assert_allclose(np.dot(Ln, P), np.dot(Dn, Ln)) """Orthonormality""" assert_allclose(Xn, np.eye(self.dim)) """Probability vector""" assert_allclose(np.sum(Ln[0, :]), 1.0) """k is not None""" Rn, Dn, Ln = rdl_decomposition(P, k=self.k) Xn = np.dot(Ln, Rn) """Right-eigenvectors""" assert_allclose(np.dot(P, Rn), np.dot(Rn, Dn)) """Left-eigenvectors""" assert_allclose(np.dot(Ln, P), np.dot(Dn, Ln)) """Orthonormality""" assert_allclose(Xn, np.eye(self.k)) """Probability vector""" assert_allclose(np.sum(Ln[0, :]), 1.0) """Reversible""" """k=None""" Rn, Dn, Ln = rdl_decomposition(P, norm='reversible') assert Dn.dtype in (np.float32, np.float64) Xn = np.dot(Ln, Rn) """Right-eigenvectors""" assert_allclose(np.dot(P, Rn), np.dot(Rn, Dn)) """Left-eigenvectors""" assert_allclose(np.dot(Ln, P), np.dot(Dn, Ln)) """Orthonormality""" assert_allclose(Xn, np.eye(self.dim)) """Probability vector""" assert_allclose(np.sum(Ln[0, :]), 1.0) """Reversibility""" assert_allclose(Ln.transpose(), mu[:, np.newaxis] * Rn) """k is not None""" Rn, Dn, Ln = rdl_decomposition(P, norm='reversible', k=self.k) Xn = np.dot(Ln, Rn) """Right-eigenvectors""" assert_allclose(np.dot(P, Rn), np.dot(Rn, Dn)) """Left-eigenvectors""" assert_allclose(np.dot(Ln, P), np.dot(Dn, Ln)) """Orthonormality""" assert_allclose(Xn, np.eye(self.k)) """Probability vector""" assert_allclose(np.sum(Ln[0, :]), 1.0) """Reversibility""" assert_allclose(Ln.transpose(), mu[:, np.newaxis] * Rn)
def test_is_reversible(sparse_mode): p = np.zeros(10) q = np.zeros(10) p[0:-1] = 0.5 q[1:] = 0.5 p[4] = 0.01 q[6] = 0.1 bdc = birth_death_chain(q, p, sparse=sparse_mode) assert_equal(sparse_mode, bdc.sparse) assert_equal(issparse(bdc.transition_matrix), sparse_mode) assert_(is_reversible(bdc.transition_matrix, bdc.stationary_distribution))
def _transition_matrix(self, msm): P = msm.transition_matrix # should be ndarray by default # assert (isinstance(P, np.ndarray)) assert (isinstance(P, np.ndarray) or isinstance(P, scipy.sparse.csr_matrix)) # shape assert (np.all(P.shape == (msm.nstates, msm.nstates))) # test transition matrix properties assert (is_transition_matrix(P)) assert (is_connected(P)) # REVERSIBLE if msm.is_reversible: assert (is_reversible(P))
def test_transition_matrix_obs(self): assert np.array_equal( self.hmsm_lag1.transition_matrix_obs().shape, (self.hmsm_lag1.nstates_obs, self.hmsm_lag1.nstates_obs)) assert np.array_equal( self.hmsm_lag10.transition_matrix_obs().shape, (self.hmsm_lag10.nstates_obs, self.hmsm_lag10.nstates_obs)) for T in [ self.hmsm_lag1.transition_matrix_obs(), self.hmsm_lag1.transition_matrix_obs(k=2), self.hmsm_lag10.transition_matrix_obs(), self.hmsm_lag10.transition_matrix_obs(k=4) ]: assert is_transition_matrix(T) assert is_reversible(T)
def _transition_matrix_samples(self, msm, given_pi): Psamples = [s.transition_matrix for s in msm.samples] # shape assert np.array_equal(np.shape(Psamples), (self.nsamples, self.n_states, self.n_states)) # consistency import deeptime.markov.tools.analysis as msmana for P in Psamples: assert msmana.is_transition_matrix(P) try: assert msmana.is_reversible(P) except AssertionError: # re-do calculation msmtools just performed to get details from deeptime.markov.tools.analysis import stationary_distribution mu = stationary_distribution(P) X = mu[:, np.newaxis] * P np.testing.assert_allclose(X, np.transpose(X), atol=1e-12, err_msg="P not reversible, given_pi={}".format(given_pi))
def test_transition_matrix(self, setting): scenario = make_double_well(setting) msm = scenario.msm P = msm.transition_matrix # should be ndarray by default # assert (isinstance(P, np.ndarray)) assert_(isinstance(P, np.ndarray) or isinstance(P, scipy.sparse.csr_matrix)) # shape assert_equal(P.shape, (msm.n_states, msm.n_states)) # test transition matrix properties import deeptime.markov.tools.analysis as msmana assert_(msmana.is_transition_matrix(P)) assert_(msmana.is_connected(P)) # REVERSIBLE if msm.reversible: assert_(msmana.is_reversible(P))
def _transition_matrix_samples(self, msm, given_pi): Psamples = msm.sample_f('transition_matrix') # shape assert np.array_equal(np.shape(Psamples), (self.nsamples, self.nstates, self.nstates)) # consistency for P in Psamples: assert is_transition_matrix(P) try: assert is_reversible(P) except AssertionError: mu = stationary_distribution(P) X = mu[:, np.newaxis] * P np.testing.assert_allclose( X, np.transpose(X), atol=1e-12, err_msg="P not reversible, given_pi={}".format(given_pi))
def P(self, value): self._P = value # check input if self._P is not None: from deeptime.markov.tools.analysis import is_transition_matrix if not is_transition_matrix(self._P, tol=1e-8): raise ValueError('T is not a transition matrix.') # set states self.nstates = _np.shape(self._P)[0] if self.reversible is None: from deeptime.markov.tools.analysis import is_reversible self.reversible = is_reversible(self._P) from scipy.sparse import issparse self.sparse = issparse(self._P) else: # set dummy values for not yet known attributes. self.nstates = 0 self.sparse = False
def test_discrete_6_3(self): # 4x4 transition matrix n_states = 3 P = np.array([[0.90, 0.10, 0.00, 0.00, 0.00, 0.00], [0.20, 0.79, 0.01, 0.00, 0.00, 0.00], [0.00, 0.01, 0.84, 0.15, 0.00, 0.00], [0.00, 0.00, 0.05, 0.94, 0.01, 0.00], [0.00, 0.00, 0.00, 0.02, 0.78, 0.20], [0.00, 0.00, 0.00, 0.00, 0.10, 0.90]]) # generate realization T = 10000 dtrajs = [MarkovStateModel(P).simulate(T)] # estimate initial HMM with 2 states - should be identical to P hmm = init.discrete.metastable_from_data(dtrajs, n_states, 1) # Test stochasticity and reversibility Tij = hmm.transition_model.transition_matrix B = hmm.output_probabilities np.testing.assert_(msmana.is_transition_matrix(Tij)) np.testing.assert_(msmana.is_reversible(Tij)) np.testing.assert_allclose(B.sum(axis=1), np.ones(B.shape[0]))
def test_IsReversible(self): # create a reversible matrix self.assertTrue(is_reversible(self.T, self.mu), "T should be reversible")
def test_is_reversible(self): self.assertTrue(is_reversible(self.T, tol=self.tol), 'matrix should be reversible')
def _pcca_connected(P, n, pi=None): r"""PCCA+ spectral clustering method with optimized memberships [1]_ Clusters the first n_cluster eigenvectors of a transition matrix in order to cluster the states. This function assumes that the transition matrix is fully connected. Parameters ---------- P : ndarray (n,n) Transition matrix. n : int Number of clusters to group to. pi: ndarray(n,), optional, default=None Stationary distribution if available. Returns ------- chi : ndarray (n x m) A matrix containing the probability or membership of each state to be assigned to each cluster. The rows sum to 1. References ---------- [1] S. Roeblitz and M. Weber, Fuzzy spectral clustering by PCCA+: application to Markov state models and data classification. Adv Data Anal Classif 7, 147-179 (2013). """ # test connectivity from deeptime.markov.tools.estimation import connected_sets labels = connected_sets(P) n_components = len( labels ) # (n_components, labels) = connected_components(P, connection='strong') if n_components > 1: raise ValueError( "Transition matrix is disconnected. Cannot use pcca_connected.") if pi is None: from deeptime.markov.tools.analysis import stationary_distribution pi = stationary_distribution(P) else: if pi.shape[0] != P.shape[0]: raise ValueError( f"Stationary distribution must span entire state space but got {pi.shape[0]} states " f"instead of {P.shape[0]}.") pi /= pi.sum() # make sure it is normalized from deeptime.markov.tools.analysis import is_reversible if not is_reversible(P, mu=pi): raise ValueError( "Transition matrix does not fulfill detailed balance. " "Make sure to call pcca with a reversible transition matrix estimate" ) # TODO: Susanna mentioned that she has a potential fix for nonreversible matrices by replacing each complex conjugate # pair by the real and imaginary components of one of the two vectors. We could use this but would then need to # orthonormalize all eigenvectors e.g. using Gram-Schmidt orthonormalization. Currently there is no theoretical # foundation for this, so I'll skip it for now. # right eigenvectors, ordered from deeptime.markov.tools.analysis import eigenvectors evecs = eigenvectors(P, n) # orthonormalize for i in range(n): evecs[:, i] /= math.sqrt(np.dot(evecs[:, i] * pi, evecs[:, i])) # make first eigenvector positive evecs[:, 0] = np.abs(evecs[:, 0]) # Is there a significant complex component? if not np.alltrue(np.isreal(evecs)): warnings.warn( "The given transition matrix has complex eigenvectors, so it doesn't exactly fulfill detailed balance. " "Forcing eigenvectors to be real and continuing. Be aware that this is not theoretically solid." ) evecs = np.real(evecs) # create initial solution using PCCA+. This could have negative memberships chi, rot_matrix = _pcca_connected_isa(evecs, n) # optimize the rotation matrix with PCCA++. rot_matrix = _opt_soft(evecs, rot_matrix, n) # These memberships should be nonnegative memberships = np.dot(evecs[:, :], rot_matrix) # We might still have numerical errors. Force memberships to be in [0,1] memberships = np.clip(memberships, 0., 1.) for i in range(0, np.shape(memberships)[0]): memberships[i] /= np.sum(memberships[i]) return memberships
def compute_reactive_flux( transition_matrix: np.ndarray, source_states: Iterable[int], target_states: Iterable[int], stationary_distribution=None, qminus=None, qplus=None, transition_matrix_tolerance: Optional[float] = None) -> ReactiveFlux: r""" Computes the A->B reactive flux using transition path theory (TPT). Parameters ---------- transition_matrix : (M, M) ndarray or scipy.sparse matrix The transition matrix. source_states : array_like List of integer state labels for set A target_states : array_like List of integer state labels for set B stationary_distribution : (M,) ndarray, optional, default=None Stationary vector. If None is computed from the transition matrix internally. qminus : (M,) ndarray (optional) Backward committor for A->B reaction qplus : (M,) ndarray (optional) Forward committor for A-> B reaction transition_matrix_tolerance : float, optional, default=None Tolerance with which is checked whether the input is actually a transition matrix. If None (default), no check is performed. Returns ------- tpt: deeptime.markov.tools.flux.ReactiveFlux object A python object containing the reactive A->B flux network and several additional quantities, such as stationary probability, committors and set definitions. Notes ----- The central object used in transition path theory is the forward and backward comittor function. TPT (originally introduced in :footcite:`weinan2006towards`) for continous systems has a discrete version outlined in :footcite:`metzner2009transition`. Here, we use the transition matrix formulation described in :footcite:`noe2009constructing`. See also -------- ReactiveFlux References ---------- .. footbibliography:: """ import deeptime.markov.tools.analysis as msmana source_states = ensure_array(source_states, dtype=int) target_states = ensure_array(target_states, dtype=int) if len(source_states) == 0 or len(target_states) == 0: raise ValueError('set A or B is empty') n_states = transition_matrix.shape[0] if len(source_states) > n_states or len(target_states) > n_states \ or max(source_states) > n_states or max(target_states) > n_states: raise ValueError( 'set A or B defines more states than the given transition matrix.') if transition_matrix_tolerance is not None and \ msmana.is_transition_matrix(transition_matrix, tol=transition_matrix_tolerance): raise ValueError('given matrix T is not a transition matrix') # we can compute the following properties from either dense or sparse T # stationary dist if stationary_distribution is None: stationary_distribution = msmana.stationary_distribution( transition_matrix) # forward committor if qplus is None: qplus = msmana.committor(transition_matrix, source_states, target_states, forward=True) # backward committor if qminus is None: if msmana.is_reversible(transition_matrix, mu=stationary_distribution): qminus = 1.0 - qplus else: qminus = msmana.committor(transition_matrix, source_states, target_states, forward=False, mu=stationary_distribution) # gross flux grossflux = tptapi.flux_matrix(transition_matrix, stationary_distribution, qminus, qplus, netflux=False) # net flux netflux = to_netflux(grossflux) # construct flux object return ReactiveFlux(source_states, target_states, net_flux=netflux, stationary_distribution=stationary_distribution, qminus=qminus, qplus=qplus, gross_flux=grossflux)