Example #1
0
    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_sample_nonrev_1(self):
        P = sample_tmatrix(self.C, reversible=False)
        assert np.all(P.shape == self.C.shape)
        assert is_transition_matrix(P)

        # same with boject
        sampler = tmatrix_sampler(self.C, reversible=False)
        P = sampler.sample()
        assert np.all(P.shape == self.C.shape)
        assert is_transition_matrix(P)
Example #3
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)
Example #4
0
 def test_discrete_4_2(self):
     # 4x4 transition matrix
     n_states = 2
     P = np.array([[0.90, 0.10, 0.00, 0.00], [0.10, 0.89, 0.01, 0.00],
                   [0.00, 0.01, 0.89, 0.10], [0.00, 0.00, 0.10, 0.90]])
     # generate realization
     T = 50000
     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,
                                              lagtime=1,
                                              regularize=False)
     # Test if model fit is close to reference. Note that we do not have an exact reference, so we cannot set the
     # tolerance in a rigorous way to test statistical significance. These are just sanity checks.
     Tij = hmm.transition_model.transition_matrix
     B = hmm.output_probabilities
     # Test stochasticity
     np.testing.assert_(msmana.is_transition_matrix(Tij))
     np.testing.assert_allclose(B.sum(axis=1), np.ones(B.shape[0]))
     Tij_ref = np.array([[0.99, 0.01], [0.01, 0.99]])
     Bref = np.array([[0.5, 0.5, 0.0, 0.0], [0.0, 0.0, 0.5, 0.5]])
     np.testing.assert_array_almost_equal(Tij, Tij_ref, decimal=2)
     if np.max(B - Bref) < .05:
         np.testing.assert_allclose(B, Bref, atol=0.06)
     else:
         np.testing.assert_allclose(B[[1, 0]], Bref, atol=0.06)
 def test_sample_nonrev_10(self):
     sampler = tmatrix_sampler(self.C, reversible=False)
     Ps = sampler.sample(nsamples=10)
     assert len(Ps) == 10
     for i in range(10):
         assert np.all(Ps[i].shape == self.C.shape)
         assert is_transition_matrix(Ps[i])
Example #6
0
def test_transition_matrix_stats(bhmm_fixture):
    stats = bhmm_fixture.bhmm.gather_stats(
        'transition_model/transition_matrix')
    import deeptime.markov.tools.analysis as msmana
    # mean
    Pmean = stats.mean
    # test shape and consistency
    assert np.array_equal(Pmean.shape,
                          (bhmm_fixture.n_states, bhmm_fixture.n_states))
    assert msmana.is_transition_matrix(Pmean)
    # std
    Pstd = stats.std
    # test shape
    assert np.array_equal(Pstd.shape,
                          (bhmm_fixture.n_states, bhmm_fixture.n_states))
    # conf
    L, R = stats.L, stats.R
    # test shape
    assert np.array_equal(L.shape,
                          (bhmm_fixture.n_states, bhmm_fixture.n_states))
    assert np.array_equal(R.shape,
                          (bhmm_fixture.n_states, bhmm_fixture.n_states))
    # test consistency
    assert np.all(L <= Pmean)
    assert np.all(R >= Pmean)
Example #7
0
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)
Example #8
0
 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))
Example #9
0
 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)
Example #10
0
 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)
Example #11
0
 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))
Example #12
0
def test_random_transition_matrix(sparse_mode):
    if sparse_mode:
        dim = 10000
        density = 0.001
    else:
        dim = 25
        density = .5
    tol = 1e-15
    A = random_nonempty_rows(dim, dim, density=density)
    T = normalize_rows(A)
    if not sparse_mode:
        T = T.toarray()
    assert_(is_transition_matrix(T, tol=tol))
Example #13
0
 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)
Example #14
0
def is_reversible(P):
    """ Returns if P is reversible on its weakly connected sets """
    import deeptime.markov.tools.analysis as msmana
    # treat each weakly connected set separately
    sets = compute_connected_sets(P, directed=False)
    for s in sets:
        Ps = P[s, :][:, s]
        if not msmana.is_transition_matrix(Ps):
            return False  # isn't even a transition matrix!
        pi = msmana.stationary_distribution(Ps)
        X = pi[:, None] * Ps
        if not np.allclose(X, X.T):
            return False
    # survived.
    return True
Example #15
0
 def test_2state_2obs_Pgiven(self):
     obs = np.array([0, 0, 1, 1, 0])
     Aref = np.array([[1.0]])
     for rev in [True,
                 False]:  # reversibiliy doesn't matter in this example
         hmm = init.discrete.metastable_from_data(obs,
                                                  n_hidden_states=1,
                                                  lagtime=1,
                                                  reversible=rev)
         np.testing.assert_(
             msmana.is_transition_matrix(
                 hmm.transition_model.transition_matrix))
         np.testing.assert_allclose(hmm.transition_model.transition_matrix,
                                    Aref)
         # output must be 1 x 2, and no zeros
         np.testing.assert_equal(hmm.output_probabilities.shape, (1, 2))
         np.testing.assert_(np.all(hmm.output_probabilities > 0))
 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))
Example #18
0
    def __init__(self,
                 T,
                 K0,
                 pi,
                 dt=1.0,
                 sparsity=None,
                 t_agg=None,
                 tol=1.0E7,
                 maxiter=100000,
                 on_error='raise'):
        from deeptime.markov.tools.analysis import is_transition_matrix

        super(CrommelinVandenEijndenEstimator,
              self).__init__(T,
                             pi,
                             dt=dt,
                             sparsity=sparsity,
                             t_agg=t_agg,
                             tol=tol,
                             maxiter=maxiter,
                             on_error=on_error)

        if not K0.shape[0] == K0.shape[1] == self.N:
            raise ValueError(
                'Shapes of K0 matrix (initial guess) and count matrix do not match.'
            )
        if not is_transition_matrix(T):
            raise_or_warn('T is not a valid transition matrix.', self.on_error)

        evals, self.U, self.Uinv = eigen_decomposition(T, self.pi)
        if not np.all(
                np.abs(evals) > 0.0):  # don't allow eigenvalue==exactly zero
            raise ValueError(
                'T has eigenvalues that are exactly zero, can\'t proceed with rate matrix estimation. '
                'If the CVE method is only used to intitialize the KL method, you might try to call the KL '
                'method with an initial guess of the rate matrix (K0) instead of intializing with CVE.'
            )
        assert np.allclose(self.Uinv.dot(T).dot(self.U),
                           np.diag(evals))  # self-consistency test

        self.c = np.abs(evals)
        self.L = np.diag(np.log(np.abs(evals)) / self.dt)

        theta = self.pi[self.I] * K0[self.I, self.J]
        self.initial = np.maximum(theta, self.lower_bounds)
Example #19
0
 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))
Example #20
0
 def _transition_matrix_stats(self, msm):
     # mean
     Pmean = msm.sample_mean('transition_matrix')
     # test shape and consistency
     assert np.array_equal(Pmean.shape, (self.nstates, self.nstates))
     assert is_transition_matrix(Pmean)
     # std
     Pstd = msm.sample_std('transition_matrix')
     # test shape
     assert np.array_equal(Pstd.shape, (self.nstates, self.nstates))
     # conf
     L, R = msm.sample_conf('transition_matrix')
     # test shape
     assert np.array_equal(L.shape, (self.nstates, self.nstates))
     assert np.array_equal(R.shape, (self.nstates, self.nstates))
     # test consistency
     assert np.all(L <= Pmean)
     assert np.all(R >= Pmean)
Example #21
0
    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 _transition_matrix_stats(self, msm):
     import deeptime.markov.tools.analysis as msmana
     # mean
     Ps = np.array([s.transition_matrix for s in msm.samples])
     Pmean = Ps.mean(axis=0)
     # test shape and consistency
     assert np.array_equal(Pmean.shape, (self.n_states, self.n_states))
     assert msmana.is_transition_matrix(Pmean)
     # std
     Pstd = Ps.std(axis=0)
     # test shape
     assert np.array_equal(Pstd.shape, (self.n_states, self.n_states))
     # conf
     L, R = confidence_interval(Ps)
     # test shape
     assert np.array_equal(L.shape, (self.n_states, self.n_states))
     assert np.array_equal(R.shape, (self.n_states, self.n_states))
     # test consistency
     assert np.all(L <= Pmean)
     assert np.all(R >= Pmean)
Example #23
0
 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]))
Example #24
0
 def test_discrete_2_2(self):
     # 2x2 transition matrix
     P = np.array([[0.99, 0.01], [0.01, 0.99]])
     # generate realization
     T = 10000
     dtrajs = [MarkovStateModel(P).simulate(T)]
     # estimate initial HMM with 2 states - should be identical to P
     init_hmm = init.discrete.metastable_from_data(dtrajs,
                                                   n_hidden_states=2,
                                                   lagtime=1)
     # test
     A = init_hmm.transition_model.transition_matrix
     B = init_hmm.output_probabilities
     # Test stochasticity
     np.testing.assert_(msmana.is_transition_matrix(A))
     np.testing.assert_allclose(B.sum(axis=1), np.ones(B.shape[0]))
     # A should be close to P
     if B[0, 0] < B[1, 0]:
         B = B[np.array([1, 0]), :]
     np.testing.assert_array_almost_equal(A, P, decimal=2)
     np.testing.assert_array_almost_equal(B, np.eye(2), decimal=2)
Example #25
0
 def test_is_transition_matrix(self):
     self.assertTrue(is_transition_matrix(self.T))
     """Larger test-case to prevent too restrictive tolerance settings"""
     X = np.random.random((2000, 2000))
     Tlarge = X / X.sum(axis=1)[:, np.newaxis]
     self.assertTrue(is_transition_matrix(Tlarge))
Example #26
0
 def test_is_transition_matrix(self):
     self.assertTrue(is_transition_matrix(self.T, tol=self.tol))
Example #27
0
    def test_mle_trev_given_pi(self):
        C = np.loadtxt(testpath + 'C_1_lag.dat')
        pi = np.loadtxt(testpath + 'pi.dat')

        T_impl_algo_dense_type_dense = impl_dense(C, pi)
        T_impl_algo_sparse_type_sparse = impl_sparse(
            scipy.sparse.csr_matrix(C), pi).toarray()
        T_Frank = impl_dense_Frank(C, pi)
        T_api_algo_dense_type_dense = apicall(C,
                                              reversible=True,
                                              mu=pi,
                                              method='dense')
        T_api_algo_sparse_type_dense = apicall(C,
                                               reversible=True,
                                               mu=pi,
                                               method='sparse')
        T_api_algo_dense_type_sparse = apicall(scipy.sparse.csr_matrix(C),
                                               reversible=True,
                                               mu=pi,
                                               method='dense').toarray()
        T_api_algo_sparse_type_sparse = apicall(scipy.sparse.csr_matrix(C),
                                                reversible=True,
                                                mu=pi,
                                                method='sparse').toarray()
        T_api_algo_auto_type_dense = apicall(C,
                                             reversible=True,
                                             mu=pi,
                                             method='auto')
        T_api_algo_auto_type_sparse = apicall(scipy.sparse.csr_matrix(C),
                                              reversible=True,
                                              mu=pi,
                                              method='auto').toarray()

        assert_allclose(T_impl_algo_dense_type_dense, T_Frank)
        assert_allclose(T_impl_algo_sparse_type_sparse, T_Frank)
        assert_allclose(T_api_algo_dense_type_dense, T_Frank)
        assert_allclose(T_api_algo_sparse_type_dense, T_Frank)
        assert_allclose(T_api_algo_dense_type_sparse, T_Frank)
        assert_allclose(T_api_algo_sparse_type_sparse, T_Frank)
        assert_allclose(T_api_algo_auto_type_dense, T_Frank)
        assert_allclose(T_api_algo_auto_type_sparse, T_Frank)

        assert is_transition_matrix(T_Frank)
        assert is_transition_matrix(T_impl_algo_dense_type_dense)
        assert is_transition_matrix(T_impl_algo_sparse_type_sparse)
        assert is_transition_matrix(T_api_algo_dense_type_dense)
        assert is_transition_matrix(T_api_algo_sparse_type_dense)
        assert is_transition_matrix(T_api_algo_dense_type_sparse)
        assert is_transition_matrix(T_api_algo_sparse_type_sparse)
        assert is_transition_matrix(T_api_algo_auto_type_dense)
        assert is_transition_matrix(T_api_algo_auto_type_sparse)

        assert_allclose(stationary_distribution(T_Frank), pi)
        assert_allclose(stationary_distribution(T_impl_algo_dense_type_dense),
                        pi)
        assert_allclose(
            stationary_distribution(T_impl_algo_sparse_type_sparse), pi)
        assert_allclose(stationary_distribution(T_api_algo_dense_type_dense),
                        pi)
        assert_allclose(stationary_distribution(T_api_algo_sparse_type_dense),
                        pi)
        assert_allclose(stationary_distribution(T_api_algo_dense_type_sparse),
                        pi)
        assert_allclose(stationary_distribution(T_api_algo_sparse_type_sparse),
                        pi)
        assert_allclose(stationary_distribution(T_api_algo_auto_type_dense),
                        pi)
        assert_allclose(stationary_distribution(T_api_algo_auto_type_sparse),
                        pi)
Example #28
0
def pcca(P: np.ndarray,
         m: int,
         pi: np.ndarray = None,
         transition_matrix_tol: float = 1e-12):
    """
    PCCA+ spectral clustering method with optimized memberships [1]_

    Clusters the first m eigenvectors of a transition matrix in order to cluster the states.
    This function does not assume that the transition matrix is fully connected. Disconnected sets
    will automatically define the first metastable states, with perfect membership assignments.

    Parameters
    ----------
    P : ndarray (n,n)
        Transition matrix.
    m : int
        Number of clusters to group to.
    pi : ndarray(n,), optional, default=None
        Stationary distribution if available. Should be defined piecewise over the connected sets.
    transition_matrix_tol : float, optional, default=1e-12
        Tolerance under which P is checked to be a transition matrix.

    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).
    [2] F. Noe, multiset PCCA and HMMs, in preparation.
    """
    # imports
    from deeptime.markov.tools.estimation import connected_sets
    from deeptime.markov.tools.analysis import eigenvalues, is_transition_matrix, hitting_probability

    # validate input
    n = np.shape(P)[0]
    if m > n:
        raise ValueError(
            f"Number of metastable states m={m} exceeds number of states of transition matrix n={n}"
        )
    if not is_transition_matrix(P, tol=transition_matrix_tol):
        raise ValueError("Input matrix is not a transition matrix.")
    if pi is not None and pi.ndim > 1:
        raise ValueError(
            "Stationary distribution must be given as one-dimensional array or left None."
        )
    if pi is not None and pi.shape[0] != n:
        raise ValueError(
            f"Stationary distribution must be defined on entire space, piecewise if the transition matrix "
            f"has multiple connected components. It covered {pi.shape[0]} != {n} states."
        )

    # prepare output
    chi = np.zeros((n, m))

    # test connectivity
    components = connected_sets(P)
    n_components = len(
        components
    )  # (n_components, labels) = connected_components(P, connection='strong')

    # store components as closed (with positive equilibrium distribution)
    # or as transition states (with vanishing equilibrium distribution)
    closed_components = []
    transition_states = []
    for i in range(n_components):
        component = components[i]  # np.argwhere(labels==i).flatten()
        rest = list(set(range(n)) - set(component))
        # is component closed?
        if np.sum(P[component, :][:, rest]) == 0:
            closed_components.append(component)
        else:
            transition_states.append(component)
    n_closed_components = len(closed_components)
    closed_states = np.concatenate(closed_components)
    if len(transition_states) == 0:
        transition_states = np.array([], dtype=int)
    else:
        transition_states = np.concatenate(transition_states)

    # check if we have enough clusters to support the disconnected sets
    if m < len(closed_components):
        raise ValueError(
            f"Number of metastable states m={m} is too small. Transition matrix "
            f"has {len(closed_components)} disconnected components.")

    # We collect eigenvalues in order to decide which
    closed_components_Psub = []
    closed_components_ev = []
    closed_components_enum = []
    for i in range(n_closed_components):
        component = closed_components[i]

        # compute eigenvalues in submatrix
        Psub = P[component, :][:, component]
        closed_components_Psub.append(Psub)
        closed_components_ev.append(eigenvalues(Psub))
        closed_components_enum.append(i * np.ones(
            (component.size, ), dtype=int))

    # flatten
    closed_components_ev_flat = np.hstack(closed_components_ev)
    closed_components_enum_flat = np.hstack(closed_components_enum)
    # which components should be clustered?
    component_indexes = closed_components_enum_flat[np.argsort(
        closed_components_ev_flat)][0:m]
    # cluster each component
    ipcca = 0
    for i in range(n_closed_components):
        component = closed_components[i]
        # how many PCCA states in this component?
        m_by_component = np.shape(np.argwhere(component_indexes == i))[0]

        # if 1, then the result is trivial
        if m_by_component == 1:
            chi[component, ipcca] = 1.0
            ipcca += 1
        elif m_by_component > 1:
            # print "submatrix: ",closed_components_Psub[i]
            chi[component, ipcca:ipcca + m_by_component] = _pcca_connected(
                closed_components_Psub[i],
                m_by_component,
                pi=None if pi is None else pi[closed_components[i]])
            ipcca += m_by_component
        else:
            raise RuntimeError(
                f"Component {i} spuriously has {m_by_component} pcca sets")

    # finally assign all transition states
    if transition_states.size > 0:
        # make all closed states absorbing, so we can see which closed state we hit first
        Pabs = P.copy()
        Pabs[closed_states, :] = 0.0
        Pabs[closed_states, closed_states] = 1.0
        for i in range(closed_states.size):
            # hitting probability to each closed state
            h = hitting_probability(Pabs, closed_states[i])
            for j in range(transition_states.size):
                # transition states belong to closed states with the hitting probability, and inherit their chi
                chi[transition_states[j]] += h[transition_states[j]] * chi[
                    closed_states[i]]

    # check if we have m metastable sets. If less than m, we must raise
    nmeta = np.count_nonzero(chi.sum(axis=0))
    if nmeta < m:
        raise RuntimeError(
            f"{m} metastable states requested, but transition matrix only has {nmeta}. "
            f"Consider using a prior or request less metastable states.")

    return chi
Example #29
0
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)