def __init__(self, A, B, flux, mu=None, qminus=None, qplus=None, gross_flux=None, dt_model='1 step'): # set data self._A = A self._B = B self._flux = flux self._mu = mu self._qminus = qminus self._qplus = qplus self._gross_flux = gross_flux from pyemma.util.units import TimeUnit self.dt_model = dt_model self._timeunit_model = TimeUnit(self.dt_model) # compute derived quantities: self._totalflux = tptapi.total_flux(flux, A) self._kAB = tptapi.rate(self._totalflux, mu, qminus)
def _estimate(self, dtrajs): # ensure right format dtrajs = ensure_dtraj_list(dtrajs) if self.init_hmsm is None: # estimate using maximum-likelihood superclass # memorize the observation state for bhmm and reset # TODO: more elegant solution is to set Estimator params only temporarily in estimate(X, **kwargs) default_connectivity = self.connectivity default_mincount_connectivity = self.mincount_connectivity default_observe_nonempty = self.observe_nonempty self.connectivity = None self.observe_nonempty = False self.mincount_connectivity = 0 self.accuracy = 1e-2 # this is sufficient for an initial guess super(BayesianHMSM, self)._estimate(dtrajs) self.connectivity = default_connectivity self.mincount_connectivity = default_mincount_connectivity self.observe_nonempty = default_observe_nonempty else: # if given another initialization, must copy its attributes # TODO: this is too tedious - need to automatize parameter+result copying between estimators. self.nstates = self.init_hmsm.nstates self.reversible = self.init_hmsm.is_reversible self.stationary = self.init_hmsm.stationary # trajectories self._dtrajs_full = self.init_hmsm._dtrajs_full self._dtrajs_lagged = self.init_hmsm._dtrajs_lagged self._observable_set = self.init_hmsm._observable_set self._dtrajs_obs = self.init_hmsm._dtrajs_obs # MLE estimation results self.likelihoods = self.init_hmsm.likelihoods # Likelihood history self.likelihood = self.init_hmsm.likelihood self.hidden_state_probabilities = self.init_hmsm.hidden_state_probabilities # gamma variables self.hidden_state_trajectories = self.init_hmsm.hidden_state_trajectories # Viterbi path self.count_matrix = self.init_hmsm.count_matrix # hidden count matrix self.initial_count = self.init_hmsm.initial_count # hidden init count self.initial_distribution = self.init_hmsm.initial_distribution self._active_set = self.init_hmsm._active_set # update HMM Model self.update_model_params( P=self.init_hmsm.transition_matrix, pobs=self.init_hmsm.observation_probabilities, dt_model=TimeUnit(self.dt_traj).get_scaled(self.lag)) # check if we have a valid initial model import msmtools.estimation as msmest if self.reversible and not msmest.is_connected(self.count_matrix): raise NotImplementedError( 'Encountered disconnected count matrix:\n ' + str(self.count_matrix) + 'with reversible Bayesian HMM sampler using lag=' + str(self.lag) + ' and stride=' + str(self.stride) + '. Consider using shorter lag, ' + 'or shorter stride (to use more of the data), ' + 'or using a lower value for mincount_connectivity.') # here we blow up the output matrix (if needed) to the FULL state space because we want to use dtrajs in the # Bayesian HMM sampler. This is just an initialization. import msmtools.estimation as msmest nstates_full = msmest.number_of_states(dtrajs) if self.nstates_obs < nstates_full: eps = 0.01 / nstates_full # default output probability, in order to avoid zero columns # full state space output matrix. make sure there are no zero columns B_init = eps * _np.ones( (self.nstates, nstates_full), dtype=_np.float64) # fill active states B_init[:, self.observable_set] = _np.maximum( eps, self.observation_probabilities) # renormalize B to make it row-stochastic B_init /= B_init.sum(axis=1)[:, None] else: B_init = self.observation_probabilities # HMM sampler if self.show_progress: self._progress_register(self.nsamples, description='Sampling HMSMs', stage=0) def call_back(): self._progress_update(1, stage=0) else: call_back = None from bhmm import discrete_hmm, bayesian_hmm hmm_mle = discrete_hmm(self.initial_distribution, self.transition_matrix, B_init) sampled_hmm = bayesian_hmm( self.discrete_trajectories_lagged, hmm_mle, nsample=self.nsamples, reversible=self.reversible, stationary=self.stationary, p0_prior=self.p0_prior, transition_matrix_prior=self.transition_matrix_prior, store_hidden=self.store_hidden, call_back=call_back) if self.show_progress: self._progress_force_finish(stage=0) # Samples sample_Ps = [ sampled_hmm.sampled_hmms[i].transition_matrix for i in range(self.nsamples) ] sample_pis = [ sampled_hmm.sampled_hmms[i].stationary_distribution for i in range(self.nsamples) ] sample_pobs = [ sampled_hmm.sampled_hmms[i].output_model.output_probabilities for i in range(self.nsamples) ] samples = [] for i in range( self.nsamples): # restrict to observable set if necessary Bobs = sample_pobs[i][:, self.observable_set] sample_pobs[i] = Bobs / Bobs.sum(axis=1)[:, None] # renormalize samples.append( _HMSM(sample_Ps[i], sample_pobs[i], pi=sample_pis[i], dt_model=self.dt_model)) # store results self.sampled_trajs = [ sampled_hmm.sampled_hmms[i].hidden_state_trajectories for i in range(self.nsamples) ] self.update_model_params(samples=samples) # deal with connectivity states_subset = None if self.connectivity == 'largest': states_subset = 'largest-strong' elif self.connectivity == 'populous': states_subset = 'populous-strong' # OBSERVATION SET if self.observe_nonempty: observe_subset = 'nonempty' else: observe_subset = None # return submodel (will return self if all None) return self.submodel(states=states_subset, obs=observe_subset, mincount_connectivity=self.mincount_connectivity)
def dt_model(self, value): self._dt_model = value # this is only used internally? from pyemma.util.units import TimeUnit self._timeunit_model = TimeUnit(self.dt_model)
def test_dt_model(self): from pyemma.util.units import TimeUnit tu = TimeUnit("4 fs").get_scaled(self.bmsm_rev.lag) self.assertEqual(self.bmsm_rev.dt_model, tu)
def dt_model(self, value): self._dt_model = value from pyemma.util.units import TimeUnit self._timeunit_model = TimeUnit(self._dt_model)
class MaximumLikelihoodHMSM(_Estimator, _EstimatedHMSM): r"""Maximum likelihood estimator for a Hidden MSM given a MSM""" def __init__(self, nstates=2, lag=1, stride=1, msm_init=None, reversible=True, connectivity='largest', observe_active=True, dt_traj='1 step', accuracy=1e-3, maxit=1000): r"""Maximum likelihood estimator for a Hidden MSM given a MSM Parameters ---------- nstates : int, optional, default=2 number of hidden states lag : int, optional, default=1 lagtime to estimate the HMSM at stride : str or int, default=1 stride between two lagged trajectories extracted from the input trajectories. Given trajectory s[t], stride and lag will result in trajectories s[0], s[lag], s[2 lag], ... s[stride], s[stride + lag], s[stride + 2 lag], ... Setting stride = 1 will result in using all data (useful for maximum likelihood estimator), while a Bayesian estimator requires a longer stride in order to have statistically uncorrelated trajectories. Setting stride = 'effective' uses the largest neglected timescale as an estimate for the correlation time and sets the stride accordingly msm_init : :class:`MSM <pyemma.msm.estimators.msm_estimated.MSM>` MSM object to initialize the estimation reversible : bool, optional, default = True If true compute reversible MSM, else non-reversible MSM connectivity : str, optional, default = 'largest' Connectivity mode. Three methods are intended (currently only 'largest' is implemented) * 'largest' : The active set is the largest reversibly connected set. All estimation will be done on this subset and all quantities (transition matrix, stationary distribution, etc) are only defined on this subset and are correspondingly smaller than the full set of states * 'all' : The active set is the full set of states. Estimation will be conducted on each reversibly connected set separately. That means the transition matrix will decompose into disconnected submatrices, the stationary vector is only defined within subsets, etc. Currently not implemented. * 'none' : The active set is the full set of states. Estimation will be conducted on the full set of states without ensuring connectivity. This only permits nonreversible estimation. Currently not implemented. observe_active : bool, optional, default=True True: Restricts the observation set to the active states of the MSM. False: All states are in the observation set. dt_traj : str, optional, default='1 step' Description of the physical time corresponding to the trajectory time step. May be used by analysis algorithms such as plotting tools to pretty-print the axes. By default '1 step', i.e. there is no physical time unit. Specify by a number, whitespace and unit. Permitted units are (* is an arbitrary string): | 'fs', 'femtosecond*' | 'ps', 'picosecond*' | 'ns', 'nanosecond*' | 'us', 'microsecond*' | 'ms', 'millisecond*' | 's', 'second*' accuracy : float, optional, default = 1e-3 convergence threshold for EM iteration. When two the likelihood does not increase by more than accuracy, the iteration is stopped successfully. maxit : int, optional, default = 1000 stopping criterion for EM iteration. When so many iterations are performed without reaching the requested accuracy, the iteration is stopped without convergence (a warning is given) """ self.nstates = nstates self.lag = lag self.stride = stride self.msm_init = msm_init self.reversible = reversible self.connectivity = connectivity self.observe_active = observe_active self.dt_traj = dt_traj self.timestep_traj = TimeUnit(dt_traj) self.accuracy = accuracy self.maxit = maxit #TODO: store_data is mentioned but not implemented or used! def _estimate(self, dtrajs): """ Parameters ---------- Return ------ hmsm : :class:`EstimatedHMSM <pyemma.msm.estimators.hmsm_estimated.EstimatedHMSM>` Estimated Hidden Markov state model """ # ensure right format dtrajs = _types.ensure_dtraj_list(dtrajs) # if no initial MSM is given, estimate it now if self.msm_init is None: # estimate with sparse=False, because we need to do PCCA which is currently not implemented for sparse # estimate with store_data=True, because we need an EstimatedMSM msm_estimator = _MSMEstimator(lag=self.lag, reversible=self.reversible, sparse=False, connectivity=self.connectivity, dt_traj=self.timestep_traj) msm_init = msm_estimator.estimate(dtrajs) else: assert isinstance(self.msm_init, _EstimatedMSM), 'msm_init must be of type EstimatedMSM' msm_init = self.msm_init self.reversible = msm_init.is_reversible # print 'Connected set: ', msm_init.active_set # generate lagged observations if self.stride == 'effective': # by default use lag as stride (=lag sampling), because we currently have no better theory for deciding # how many uncorrelated counts we can make self.stride = self.lag # if we have more than nstates timescales in our MSM, we use the next (neglected) timescale as an # estimate of the decorrelation time if msm_init.nstates > self.nstates: corrtime = int(max(1, msm_init.timescales()[self.nstates-1])) # use the smaller of these two pessimistic estimates self.stride = min(self.stride, 2*corrtime) # TODO: Here we always use the full observation state space for the estimation. dtrajs_lagged = _lag_observations(dtrajs, self.lag, stride=self.stride) # check input assert _types.is_int(self.nstates) and self.nstates > 1 and self.nstates <= msm_init.nstates, \ 'nstates must be an int in [2,msmobj.nstates]' # if hmm.nstates = msm.nstates there is no problem. Otherwise, check spectral gap if msm_init.nstates > self.nstates: timescale_ratios = msm_init.timescales()[:-1] / msm_init.timescales()[1:] if timescale_ratios[self.nstates-2] < 2.0: self.logger.warn('Requested coarse-grained model with ' + str(self.nstates) + ' metastable states at ' + 'lag=' + str(self.lag) + '.' + 'The ratio of relaxation timescales between ' + str(self.nstates) + ' and ' + str(self.nstates+1) + ' states is only ' + str(timescale_ratios[self.nstates-2]) + ' while we recommend at least 2. ' + ' It is possible that the resulting HMM is inaccurate. Handle with caution.') # set things from MSM # TODO: dtrajs_obs is set here, but not used in estimation. Estimation is alwas done with # TODO: respect to full observation (see above). This is confusing. Define how we want to do this in gen. # TODO: observable set is also not used, it is just saved. nstates_obs_full = msm_init.nstates_full if self.observe_active: nstates_obs = msm_init.nstates observable_set = msm_init.active_set dtrajs_obs = msm_init.discrete_trajectories_active else: nstates_obs = msm_init.nstates_full observable_set = np.arange(nstates_obs_full) dtrajs_obs = msm_init.discrete_trajectories_full # TODO: this is redundant with BHMM code because that code is currently not easily accessible and # TODO: we don't want to re-estimate. Should be reengineered in bhmm. # --------------------------------------------------------------------------------------- # PCCA-based coarse-graining # --------------------------------------------------------------------------------------- # pcca- to number of metastable states pcca = msm_init.pcca(self.nstates) # HMM output matrix eps = 0.01 * (1.0/nstates_obs_full) # default output probability, in order to avoid zero columns # Use PCCA distributions, but at least eps to avoid 100% assignment to any state (breaks convergence) B_conn = np.maximum(msm_init.metastable_distributions, eps) # full state space output matrix B = eps * np.ones((self.nstates, nstates_obs_full), dtype=np.float64) # expand B_conn to full state space # TODO: here we always select the active set, no matter if observe_active=True or False. B[:, msm_init.active_set] = B_conn[:, :] # TODO: at this point we will have zero observation probabilities for states that are not in the active # TODO: set. If these occur in the trajectory, that will mean zero columns in the output probabilities # TODO: and crash of forward-backward and sampling algorithms. # renormalize B to make it row-stochastic B /= B.sum(axis=1)[:, None] # coarse-grained transition matrix P_coarse = pcca.coarse_grained_transition_matrix # take care of unphysical values. First symmetrize X = np.dot(np.diag(pcca.coarse_grained_stationary_probability), P_coarse) X = 0.5*(X + X.T) # if there are values < 0, set to eps X = np.maximum(X, eps) # turn into coarse-grained transition matrix A = X / X.sum(axis=1)[:, None] # --------------------------------------------------------------------------------------- # Estimate discrete HMM # --------------------------------------------------------------------------------------- # lazy import bhmm here in order to avoid dependency loops import bhmm # initialize discrete HMM hmm_init = bhmm.discrete_hmm(A, B, stationary=True, reversible=self.reversible) # run EM hmm = bhmm.estimate_hmm(dtrajs_lagged, self.nstates, lag=1, initial_model=hmm_init, accuracy=self.accuracy, maxit=self.maxit) self.hmm = bhmm.DiscreteHMM(hmm) # find observable set transition_matrix = self.hmm.transition_matrix observation_probabilities = self.hmm.output_probabilities # TODO: Cutting down... OK, this can be done if self.observe_active: # cut down observation probabilities to active set observation_probabilities = observation_probabilities[:, msm_init.active_set] observation_probabilities /= observation_probabilities.sum(axis=1)[:,None] # renormalize # parametrize self self._dtrajs_full = dtrajs self._dtrajs_lagged = dtrajs_lagged self._observable_set = observable_set self._dtrajs_obs = dtrajs_obs self.set_model_params(P=transition_matrix, pobs=observation_probabilities, reversible=self.reversible, dt_model=self.timestep_traj.get_scaled(self.lag)) return self def cktest(self, mlags=10, conf=0.95, err_est=False, show_progress=True): """ Conducts a Chapman-Kolmogorow test. Parameters ---------- mlags : int or int-array, default=10 multiples of lag times for testing the Model, e.g. range(10). A single int will trigger a range, i.e. mlags=10 maps to mlags=range(10). The setting None will choose mlags automatically according to the longest available trajectory conf : float, optional, default = 0.95 confidence interval err_est : bool, default=False compute errors also for all estimations (computationally expensive) If False, only the prediction will get error bars, which is often sufficient to validate a model. show_progress : bool, default=True Show progressbars for calculation? References ---------- This is an adaption of the Chapman-Kolmogorov Test described in detail in [1]_ to Hidden MSMs as described in [2]_. .. [1] Prinz, J H, H Wu, M Sarich, B Keller, M Senne, M Held, J D Chodera, C Schuette and F Noe. 2011. Markov models of molecular kinetics: Generation and validation. J Chem Phys 134: 174105 .. [2] F. Noe, H. Wu, J.-H. Prinz and N. Plattner: Projected and hidden Markov models for calculating kinetics and metastable states of complex molecules. J. Chem. Phys. 139, 184114 (2013) """ from pyemma.msm.estimators import ChapmanKolmogorovValidator ck = ChapmanKolmogorovValidator(self, self, np.eye(self.nstates), mlags=mlags, conf=conf, err_est=err_est, show_progress=show_progress) ck.estimate(self._dtrajs_full) return ck
def test_dt_model(self): from pyemma.util.units import TimeUnit tu = TimeUnit("1 step").get_scaled(self.hmsm_lag10.lag) self.assertEqual(self.hmsm_lag10.dt_model, tu)
def dt_traj(self, value): self._dt_traj = value self.timestep_traj = TimeUnit(value)
class MaximumLikelihoodHMSM(_Estimator, _HMSM): r"""Maximum likelihood estimator for a Hidden MSM given a MSM""" def __init__(self, nstates=2, lag=1, stride=1, msm_init='largest-strong', reversible=True, stationary=False, connectivity=None, mincount_connectivity='1/n', observe_nonempty=True, separate=None, dt_traj='1 step', accuracy=1e-3, maxit=1000): r"""Maximum likelihood estimator for a Hidden MSM given a MSM Parameters ---------- nstates : int, optional, default=2 number of hidden states lag : int, optional, default=1 lagtime to estimate the HMSM at stride : str or int, default=1 stride between two lagged trajectories extracted from the input trajectories. Given trajectory s[t], stride and lag will result in trajectories s[0], s[lag], s[2 lag], ... s[stride], s[stride + lag], s[stride + 2 lag], ... Setting stride = 1 will result in using all data (useful for maximum likelihood estimator), while a Bayesian estimator requires a longer stride in order to have statistically uncorrelated trajectories. Setting stride = 'effective' uses the largest neglected timescale as an estimate for the correlation time and sets the stride accordingly msm_init : str or :class:`MSM <pyemma.msm.MSM>` MSM object to initialize the estimation, or one of following keywords: * 'largest-strong' or None (default) : Estimate MSM on the largest strongly connected set and use spectral clustering to generate an initial HMM * 'all' : Estimate MSM(s) on the full state space to initialize the HMM. This estimate maybe weakly connected or disconnected. reversible : bool, optional, default = True If true compute reversible MSM, else non-reversible MSM stationary : bool, optional, default=False If True, the initial distribution of hidden states is self-consistently computed as the stationary distribution of the transition matrix. If False, it will be estimated from the starting states. Only set this to true if you're sure that the observation trajectories are initiated from a global equilibrium distribution. connectivity : str, optional, default = None Defines if the resulting HMM will be defined on all hidden states or on a connected subset. Connectivity is defined by counting only transitions with at least mincount_connectivity counts. If a subset of states is used, all estimated quantities (transition matrix, stationary distribution, etc) are only defined on this subset and are correspondingly smaller than nstates. Following modes are available: * None or 'all' : The active set is the full set of states. Estimation is done on all weakly connected subsets separately. The resulting transition matrix may be disconnected. * 'largest' : The active set is the largest reversibly connected set. * 'populous' : The active set is the reversibly connected set with most counts. mincount_connectivity : float or '1/n' minimum number of counts to consider a connection between two states. Counts lower than that will count zero in the connectivity check and may thus separate the resulting transition matrix. The default evaluates to 1/nstates. separate : None or iterable of int Force the given set of observed states to stay in a separate hidden state. The remaining nstates-1 states will be assigned by a metastable decomposition. observe_nonempty : bool If True, will restricted the observed states to the states that have at least one observation in the lagged input trajectories. If an initial MSM is given, this option is ignored and the observed subset is always identical to the active set of that MSM. dt_traj : str, optional, default='1 step' Description of the physical time corresponding to the trajectory time step. May be used by analysis algorithms such as plotting tools to pretty-print the axes. By default '1 step', i.e. there is no physical time unit. Specify by a number, whitespace and unit. Permitted units are (* is an arbitrary string): | 'fs', 'femtosecond*' | 'ps', 'picosecond*' | 'ns', 'nanosecond*' | 'us', 'microsecond*' | 'ms', 'millisecond*' | 's', 'second*' accuracy : float, optional, default = 1e-3 convergence threshold for EM iteration. When two the likelihood does not increase by more than accuracy, the iteration is stopped successfully. maxit : int, optional, default = 1000 stopping criterion for EM iteration. When so many iterations are performed without reaching the requested accuracy, the iteration is stopped without convergence (a warning is given) """ self.nstates = nstates self.lag = lag self.stride = stride self.msm_init = msm_init self.reversible = reversible self.stationary = stationary self.connectivity = connectivity if mincount_connectivity == '1/n': mincount_connectivity = 1.0/float(nstates) self.mincount_connectivity = mincount_connectivity self.separate = separate self.observe_nonempty = observe_nonempty self.dt_traj = dt_traj self.timestep_traj = TimeUnit(dt_traj) self.accuracy = accuracy self.maxit = maxit #TODO: store_data is mentioned but not implemented or used! def _estimate(self, dtrajs): import bhmm # ensure right format dtrajs = _types.ensure_dtraj_list(dtrajs) # CHECK LAG trajlengths = [_np.size(dtraj) for dtraj in dtrajs] if self.lag >= _np.max(trajlengths): raise ValueError('Illegal lag time ' + str(self.lag) + ' exceeds longest trajectory length') if self.lag > _np.mean(trajlengths): self.logger.warning('Lag time ' + str(self.lag) + ' is on the order of mean trajectory length ' + str(_np.mean(trajlengths)) + '. It is recommended to fit four lag times in each ' + 'trajectory. HMM might be inaccurate.') # EVALUATE STRIDE if self.stride == 'effective': # by default use lag as stride (=lag sampling), because we currently have no better theory for deciding # how many uncorrelated counts we can make self.stride = self.lag # get a quick estimate from the spectral radius of the nonreversible from pyemma.msm import estimate_markov_model msm_nr = estimate_markov_model(dtrajs, lag=self.lag, reversible=False, sparse=False, connectivity='largest', dt_traj=self.timestep_traj) # if we have more than nstates timescales in our MSM, we use the next (neglected) timescale as an # estimate of the decorrelation time if msm_nr.nstates > self.nstates: corrtime = max(1, msm_nr.timescales()[self.nstates-1]) # use the smaller of these two pessimistic estimates self.stride = int(min(self.lag, 2*corrtime)) # LAG AND STRIDE DATA dtrajs_lagged_strided = bhmm.lag_observations(dtrajs, self.lag, stride=self.stride) # OBSERVATION SET if self.observe_nonempty: observe_subset = 'nonempty' else: observe_subset = None # INIT HMM from bhmm import init_discrete_hmm from pyemma.msm.estimators import MaximumLikelihoodMSM if self.msm_init=='largest-strong': hmm_init = init_discrete_hmm(dtrajs_lagged_strided, self.nstates, lag=1, reversible=self.reversible, stationary=True, regularize=True, method='lcs-spectral', separate=self.separate) elif self.msm_init=='all': hmm_init = init_discrete_hmm(dtrajs_lagged_strided, self.nstates, lag=1, reversible=self.reversible, stationary=True, regularize=True, method='spectral', separate=self.separate) elif issubclass(self.msm_init.__class__, MaximumLikelihoodMSM): # initial MSM given. from bhmm.init.discrete import init_discrete_hmm_spectral p0, P0, pobs0 = init_discrete_hmm_spectral(self.msm_init.count_matrix_full, self.nstates, reversible=self.reversible, stationary=True, active_set=self.msm_init.active_set, P=self.msm_init.transition_matrix, separate=self.separate) hmm_init = bhmm.discrete_hmm(p0, P0, pobs0) observe_subset = self.msm_init.active_set # override observe_subset. else: raise ValueError('Unknown MSM initialization option: ' + str(self.msm_init)) # --------------------------------------------------------------------------------------- # Estimate discrete HMM # --------------------------------------------------------------------------------------- # run EM from bhmm.estimators.maximum_likelihood import MaximumLikelihoodEstimator as _MaximumLikelihoodEstimator hmm_est = _MaximumLikelihoodEstimator(dtrajs_lagged_strided, self.nstates, initial_model=hmm_init, output='discrete', reversible=self.reversible, stationary=self.stationary, accuracy=self.accuracy, maxit=self.maxit) # run hmm_est.fit() # package in discrete HMM self.hmm = bhmm.DiscreteHMM(hmm_est.hmm) # get model parameters self.initial_distribution = self.hmm.initial_distribution transition_matrix = self.hmm.transition_matrix observation_probabilities = self.hmm.output_probabilities # get estimation parameters self.likelihoods = hmm_est.likelihoods # Likelihood history self.likelihood = self.likelihoods[-1] self.hidden_state_probabilities = hmm_est.hidden_state_probabilities # gamma variables self.hidden_state_trajectories = hmm_est.hmm.hidden_state_trajectories # Viterbi path self.count_matrix = hmm_est.count_matrix # hidden count matrix self.initial_count = hmm_est.initial_count # hidden init count self._active_set = _np.arange(self.nstates) # TODO: it can happen that we loose states due to striding. Should we lift the output probabilities afterwards? # parametrize self self._dtrajs_full = dtrajs self._dtrajs_lagged = dtrajs_lagged_strided self._nstates_obs_full = msmest.number_of_states(dtrajs) self._nstates_obs = msmest.number_of_states(dtrajs_lagged_strided) self._observable_set = _np.arange(self._nstates_obs) self._dtrajs_obs = dtrajs self.set_model_params(P=transition_matrix, pobs=observation_probabilities, reversible=self.reversible, dt_model=self.timestep_traj.get_scaled(self.lag)) # TODO: perhaps remove connectivity and just rely on .submodel()? # deal with connectivity states_subset = None if self.connectivity == 'largest': states_subset = 'largest-strong' elif self.connectivity == 'populous': states_subset = 'populous-strong' # return submodel (will return self if all None) return self.submodel(states=states_subset, obs=observe_subset, mincount_connectivity=self.mincount_connectivity) @property def lagtime(self): """ The lag time in steps """ return self.lag @property def nstates_obs(self): r""" Number of states in discrete trajectories """ return self._nstates_obs @property def active_set(self): """ The active set of hidden states on which all hidden state computations are done """ if hasattr(self, '_active_set'): return self._active_set else: return _np.arange(self.nstates) # all hidden states are active. @property def observable_set(self): """ The active set of states on which all computations and estimations will be done """ return self._observable_set @property @alias('dtrajs_full') def discrete_trajectories_full(self): """ A list of integer arrays with the original trajectories. """ return self._dtrajs_full @property @alias('dtrajs_lagged') def discrete_trajectories_lagged(self): """ Transformed original trajectories that are used as an input into the HMM estimation """ return self._dtrajs_lagged @property @alias('dtrajs_obs') def discrete_trajectories_obs(self): """ A list of integer arrays with the discrete trajectories mapped to the observation mode used. When using observe_active = True, the indexes will be given on the MSM active set. Frames that are not in the observation set will be -1. When observe_active = False, this attribute is identical to discrete_trajectories_full """ return self._dtrajs_obs ################################################################################ # Submodel functions using estimation information (counts) ################################################################################ def submodel(self, states=None, obs=None, mincount_connectivity='1/n'): """Returns a HMM with restricted state space Parameters ---------- states : None, str or int-array Hidden states to restrict the model to. In addition to specifying the subset, possible options are: * None : all states - don't restrict * 'populous-strong' : strongly connected subset with maximum counts * 'populous-weak' : weakly connected subset with maximum counts * 'largest-strong' : strongly connected subset with maximum size * 'largest-weak' : weakly connected subset with maximum size obs : None, str or int-array Observed states to restrict the model to. In addition to specifying an array with the state labels to be observed, possible options are: * None : all states - don't restrict * 'nonempty' : all states with at least one observation in the estimator mincount_connectivity : float or '1/n' minimum number of counts to consider a connection between two states. Counts lower than that will count zero in the connectivity check and may thus separate the resulting transition matrix. Default value: 1/nstates. Returns ------- hmm : HMM The restricted HMM. """ if states is None and obs is None and mincount_connectivity == 0: return self if states is None: states = _np.arange(self.nstates) if obs is None: obs = _np.arange(self.nstates_obs) if str(mincount_connectivity) == '1/n': mincount_connectivity = 1.0/float(self.nstates) # handle new connectivity from bhmm.estimators import _tmatrix_disconnected S = _tmatrix_disconnected.connected_sets(self.count_matrix, mincount_connectivity=mincount_connectivity, strong=True) if len(S) > 1: # keep only non-negligible transitions C = _np.zeros(self.count_matrix.shape) large = _np.where(self.count_matrix >= mincount_connectivity) C[large] = self.count_matrix[large] for s in S: # keep all (also small) transition counts within strongly connected subsets C[_np.ix_(s, s)] = self.count_matrix[_np.ix_(s, s)] # re-estimate transition matrix with disc. P = _tmatrix_disconnected.estimate_P(C, reversible=self.reversible, mincount_connectivity=0) pi = _tmatrix_disconnected.stationary_distribution(P, C) else: C = self.count_matrix P = self.transition_matrix pi = self.stationary_distribution # determine substates if isinstance(states, str): from bhmm.estimators import _tmatrix_disconnected strong = 'strong' in states largest = 'largest' in states S = _tmatrix_disconnected.connected_sets(self.count_matrix, mincount_connectivity=mincount_connectivity, strong=strong) if largest: score = [len(s) for s in S] else: score = [self.count_matrix[_np.ix_(s, s)].sum() for s in S] states = _np.array(S[_np.argmax(score)]) if states is not None: # sub-transition matrix self._active_set = states C = C[_np.ix_(states, states)].copy() P = P[_np.ix_(states, states)].copy() P /= P.sum(axis=1)[:, None] pi = _tmatrix_disconnected.stationary_distribution(P, C) self.initial_count = self.initial_count[states] self.initial_distribution = self.initial_distribution[states] / self.initial_distribution[states].sum() # determine observed states if str(obs) == 'nonempty': import msmtools.estimation as msmest obs = _np.where(msmest.count_states(self.discrete_trajectories_lagged) > 0)[0] if obs is not None: # set observable set self._observable_set = obs self._nstates_obs = obs.size # full2active mapping _full2obs = -1 * _np.ones(self._nstates_obs_full, dtype=int) _full2obs[obs] = _np.arange(len(obs), dtype=int) # observable trajectories self._dtrajs_obs = [] for dtraj in self.discrete_trajectories_full: self._dtrajs_obs.append(_full2obs[dtraj]) # observation matrix B = self.observation_probabilities[_np.ix_(states, obs)].copy() B /= B.sum(axis=1)[:, None] else: B = self.observation_probabilities # set quantities back. self.update_model_params(P=P, pobs=B, pi=pi) self.count_matrix_EM = self.count_matrix[_np.ix_(states, states)] # unchanged count matrix self.count_matrix = C # count matrix consistent with P return self def submodel_largest(self, strong=True, mincount_connectivity='1/n'): """ Returns the largest connected sub-HMM (convenience function) Returns ------- hmm : HMM The restricted HMM. """ if strong: return self.submodel(states='largest-strong', mincount_connectivity=mincount_connectivity) else: return self.submodel(states='largest-weak', mincount_connectivity=mincount_connectivity) def submodel_populous(self, strong=True, mincount_connectivity='1/n'): """ Returns the most populous connected sub-HMM (convenience function) Returns ------- hmm : HMM The restricted HMM. """ if strong: return self.submodel(states='populous-strong', mincount_connectivity=mincount_connectivity) else: return self.submodel(states='populous-weak', mincount_connectivity=mincount_connectivity) def submodel_disconnect(self, mincount_connectivity='1/n'): """Disconnects sets of hidden states that are barely connected Runs a connectivity check excluding all transition counts below mincount_connectivity. The transition matrix and stationary distribution will be re-estimated. Note that the resulting transition matrix may have both strongly and weakly connected subsets. Parameters ---------- mincount_connectivity : float or '1/n' minimum number of counts to consider a connection between two states. Counts lower than that will count zero in the connectivity check and may thus separate the resulting transition matrix. The default evaluates to 1/nstates. Returns ------- hmm : HMM The restricted HMM. """ return self.submodel(mincount_connectivity=mincount_connectivity) def trajectory_weights(self): r"""Uses the HMSM to assign a probability weight to each trajectory frame. This is a powerful function for the calculation of arbitrary observables in the trajectories one has started the analysis with. The stationary probability of the MSM will be used to reweigh all states. Returns a list of weight arrays, one for each trajectory, and with a number of elements equal to trajectory frames. Given :math:`N` trajectories of lengths :math:`T_1` to :math:`T_N`, this function returns corresponding weights: .. math:: (w_{1,1}, ..., w_{1,T_1}), (w_{N,1}, ..., w_{N,T_N}) that are normalized to one: .. math:: \sum_{i=1}^N \sum_{t=1}^{T_i} w_{i,t} = 1 Suppose you are interested in computing the expectation value of a function :math:`a(x)`, where :math:`x` are your input configurations. Use this function to compute the weights of all input configurations and obtain the estimated expectation by: .. math:: \langle a \rangle = \sum_{i=1}^N \sum_{t=1}^{T_i} w_{i,t} a(x_{i,t}) Or if you are interested in computing the time-lagged correlation between functions :math:`a(x)` and :math:`b(x)` you could do: .. math:: \langle a(t) b(t+\tau) \rangle_t = \sum_{i=1}^N \sum_{t=1}^{T_i} w_{i,t} a(x_{i,t}) a(x_{i,t+\tau}) Returns ------- The normalized trajectory weights. Given :math:`N` trajectories of lengths :math:`T_1` to :math:`T_N`, returns the corresponding weights: .. math:: (w_{1,1}, ..., w_{1,T_1}), (w_{N,1}, ..., w_{N,T_N}) """ # compute stationary distribution, expanded to full set statdist = self.stationary_distribution_obs statdist = _np.append(statdist, [-1]) # add a zero weight at index -1, to deal with unobserved states # histogram observed states import msmtools.dtraj as msmtraj hist = 1.0 * msmtraj.count_states(self.discrete_trajectories_obs, ignore_negative=True) # simply read off stationary distribution and accumulate total weight W = [] wtot = 0.0 for dtraj in self.discrete_trajectories_obs: w = statdist[dtraj] / hist[dtraj] W.append(w) wtot += _np.sum(w) # normalize for w in W: w /= wtot # done return W ################################################################################ # Generation of trajectories and samples ################################################################################ @property def observable_state_indexes(self): """ Ensures that the observable states are indexed and returns the indices """ try: # if we have this attribute, return it return self._observable_state_indexes except AttributeError: # didn't exist? then create it. import pyemma.util.discrete_trajectories as dt self._observable_state_indexes = dt.index_states(self.discrete_trajectories_obs) return self._observable_state_indexes # TODO: generate_traj. How should that be defined? Probably indexes of observable states, but should we specify # hidden or observable states as start and stop states? # TODO: sample_by_state. How should that be defined? def sample_by_observation_probabilities(self, nsample): r"""Generates samples according to given probability distributions Parameters ---------- distributions : list or array of ndarray ( (n) ) m distributions over states. Each distribution must be of length n and must sum up to 1.0 nsample : int Number of samples per distribution. If replace = False, the number of returned samples per state could be smaller if less than nsample indexes are available for a state. Returns ------- indexes : length m list of ndarray( (nsample, 2) ) List of the sampled indices by distribution. Each element is an index array with a number of rows equal to nsample, with rows consisting of a tuple (i, t), where i is the index of the trajectory and t is the time index within the trajectory. """ import pyemma.util.discrete_trajectories as dt return dt.sample_indexes_by_distribution(self.observable_state_indexes, self.observation_probabilities, nsample) ################################################################################ # Model Validation ################################################################################ def cktest(self, mlags=10, conf=0.95, err_est=False, show_progress=True): """ Conducts a Chapman-Kolmogorow test. Parameters ---------- mlags : int or int-array, default=10 multiples of lag times for testing the Model, e.g. range(10). A single int will trigger a range, i.e. mlags=10 maps to mlags=range(10). The setting None will choose mlags automatically according to the longest available trajectory conf : float, optional, default = 0.95 confidence interval err_est : bool, default=False compute errors also for all estimations (computationally expensive) If False, only the prediction will get error bars, which is often sufficient to validate a model. show_progress : bool, default=True Show progressbars for calculation? Returns ------- cktest : :class:`ChapmanKolmogorovValidator <pyemma.msm.ChapmanKolmogorovValidator>` References ---------- This is an adaption of the Chapman-Kolmogorov Test described in detail in [1]_ to Hidden MSMs as described in [2]_. .. [1] Prinz, J H, H Wu, M Sarich, B Keller, M Senne, M Held, J D Chodera, C Schuette and F Noe. 2011. Markov models of molecular kinetics: Generation and validation. J Chem Phys 134: 174105 .. [2] F. Noe, H. Wu, J.-H. Prinz and N. Plattner: Projected and hidden Markov models for calculating kinetics and metastable states of complex molecules. J. Chem. Phys. 139, 184114 (2013) """ from pyemma.msm.estimators import ChapmanKolmogorovValidator ck = ChapmanKolmogorovValidator(self, self, _np.eye(self.nstates), mlags=mlags, conf=conf, err_est=err_est, show_progress=show_progress) ck.estimate(self._dtrajs_full) return ck
def __init__(self, nstates=2, lag=1, stride='effective', prior='mixed', nsamples=100, init_hmsm=None, reversible=True, connectivity='largest', observe_active=True, dt_traj='1 step', conf=0.95, show_progress=True): r"""Estimator for a Bayesian HMSM Parameters ---------- nstates : int, optional, default=2 number of hidden states lag : int, optional, default=1 lagtime to estimate the HMSM at stride : str or int, default=1 stride between two lagged trajectories extracted from the input trajectories. Given trajectory s[t], stride and lag will result in trajectories s[0], s[tau], s[2 tau], ... s[stride], s[stride + tau], s[stride + 2 tau], ... Setting stride = 1 will result in using all data (useful for maximum likelihood estimator), while a Bayesian estimator requires a longer stride in order to have statistically uncorrelated trajectories. Setting stride = None 'effective' uses the largest neglected timescale as an estimate for the correlation time and sets the stride accordingly. prior : str, optional, default='mixed' prior used in the estimation of the transition matrix. While 'sparse' would be preferred as it doesn't bias the distribution way from the maximum-likelihood, this prior is sensitive to loss of connectivity. Loss of connectivity can occur in the Gibbs sampling algorithm used here because in each iteration the hidden state sequence is randomly generated. Once full connectivity is lost in one of these steps, the current algorithm cannot recover from that. As a solution we suggest using a prior that ensures that the estimated transition matrix is connected even if the sampled state sequence is not. * 'sparse' : the sparse prior proposed in [1]_ which centers the posterior around the maximum likelihood estimator. This is the preferred option if there are no connectivity problems. However this prior is sensitive to loss of connectivity. * 'uniform' : uniform prior probability for every transition matrix element. Compared to the sparse prior, 'uniform' adds +1 to every transition count. Weak prior that ensures connectivity, but can lead to large biases if some states have small exit probabilities. * 'mixed' : ensures connectivity by adding a prior taken from the maximum likelihood estimate (MLE) of the hidden transition matrix P. The rows of P are scaled in order to have total outgoing transition counts of at least 1 out of each state. While this operation centers the posterior around the MLE, it can be a very strong prior if states with small exit probabilities are involved, and can therefore artificially reduce the error bars. init_hmsm : :class:`HMSM <pyemma.msm.models.HMSM>`, default=None Single-point estimate of HMSM object around which errors will be evaluated. If None is give an initial estimate will be automatically generated using the given parameters. observe_active : bool, optional, default=True True: Restricts the observation set to the active states of the MSM. False: All states are in the observation set. show_progress : bool, default=True Show progressbars for calculation? References ---------- .. [1] F. Noe, H. Wu, J.-H. Prinz and N. Plattner: Projected and hidden Markov models for calculating kinetics and metastable states of complex molecules. J. Chem. Phys. 139, 184114 (2013) .. [2] J. D. Chodera Et Al: Bayesian hidden Markov model analysis of single-molecule force spectroscopy: Characterizing kinetics under measurement uncertainty. arXiv:1108.1430 (2011) """ self.lag = lag self.stride = stride self.nstates = nstates self.prior = prior self.nsamples = nsamples self.init_hmsm = init_hmsm self.reversible = reversible self.connectivity = connectivity self.observe_active = observe_active self.dt_traj = dt_traj self.timestep_traj = TimeUnit(dt_traj) self.conf = conf self.show_progress = show_progress
def set_model_params(self, P=None, pi=None, reversible=None, dt_model='1 step', neig=None): """ Call to set all basic model parameters. Sets or updates given model parameters. This argument list of this method must contain the full list of essential, or independent model parameters. It can additionally contain derived parameters, e.g. in order to save computational costs of re-computing them. Parameters ---------- P : ndarray(n,n) transition matrix pi : ndarray(n), optional, default=None stationary distribution. Can be optionally given in case if it was already computed, e.g. by the estimator. reversible : bool, optional, default=None whether P is reversible with respect to its stationary distribution. If None (default), will be determined from P dt_model : str, optional, default='1 step' Description of the physical time corresponding to the model time step. May be used by analysis algorithms such as plotting tools to pretty-print the axes. By default '1 step', i.e. there is no physical time unit. Specify by a number, whitespace and unit. Permitted units are (* is an arbitrary string): | 'fs', 'femtosecond*' | 'ps', 'picosecond*' | 'ns', 'nanosecond*' | 'us', 'microsecond*' | 'ms', 'millisecond*' | 's', 'second*' neig : int or None The number of eigenvalues / eigenvectors to be kept. If set to None, defaults will be used. For a dense MSM the default is all eigenvalues. For a sparse MSM the default is 10. Notes ----- Explicitly define all independent model parameters in the argument list of this function (by mandatory or keyword arguments) """ import msmtools.analysis as msmana # check input if P is not None: if not msmana.is_transition_matrix(P, tol=1e-8): raise ValueError('T is not a transition matrix.') # update all parameters self.update_model_params(P=P, pi=pi, reversible=reversible, dt_model=dt_model, neig=neig) # set ncv for consistency if not hasattr(self, 'ncv'): self.ncv = None # update derived quantities from pyemma.util.units import TimeUnit self._timeunit_model = TimeUnit(self.dt_model) # set P and derived quantities if available if P is not None: from scipy.sparse import issparse # set states self._nstates = np.shape(P)[0] if self.reversible is None: self.reversible = msmana.is_reversible(P) self.sparse = issparse(P) # set or correct eig param if neig is None: if self.sparse: self.neig = 10 else: self.neig = self._nstates
def _estimate(self, dtrajs): # ensure right format dtrajs = ensure_dtraj_list(dtrajs) if self.init_hmsm is None: # estimate using maximum-likelihood superclass # memorize the observation state for bhmm and reset # TODO: more elegant solution is to set Estimator params only temporarily in estimate(X, **kwargs) default_connectivity = self.connectivity default_mincount_connectivity = self.mincount_connectivity default_observe_nonempty = self.observe_nonempty self.connectivity = None self.observe_nonempty = False self.mincount_connectivity = 0 self.accuracy = 1e-2 # this is sufficient for an initial guess super(BayesianHMSM, self)._estimate(dtrajs) self.connectivity = default_connectivity self.mincount_connectivity = default_mincount_connectivity self.observe_nonempty = default_observe_nonempty else: # if given another initialization, must copy its attributes copy_attributes = ['_nstates', '_reversible', '_pi', '_observable_set', 'likelihoods', 'likelihood', 'hidden_state_probabilities', 'hidden_state_trajectories', 'count_matrix', 'initial_count', 'initial_distribution', '_active_set'] check_user_choices = ['lag', '_nstates'] # check if nstates and lag are compatible for attr in check_user_choices: if not getattr(self, attr) == getattr(self.init_hmsm, attr): raise UserWarning('BayesianHMSM cannot be initialized with init_hmsm with ' 'incompatible lag or nstates.') if (len(dtrajs) != len(self.init_hmsm.dtrajs_full) or not all((_np.array_equal(d1, d2) for d1, d2 in zip(dtrajs, self.init_hmsm.dtrajs_full)))): raise NotImplementedError('Bayesian HMM estimation with init_hmsm is currently only implemented ' + 'if applied to the same data.') # TODO: implement more elegant solution to copy-pasting effective stride evaluation from ML HMM. # EVALUATE STRIDE if self.stride == 'effective': # by default use lag as stride (=lag sampling), because we currently have no better theory for deciding # how many uncorrelated counts we can make self.stride = self.lag # get a quick estimate from the spectral radius of the nonreversible from pyemma.msm import estimate_markov_model msm_nr = estimate_markov_model(dtrajs, lag=self.lag, reversible=False, sparse=False, connectivity='largest', dt_traj=self.timestep_traj) # if we have more than nstates timescales in our MSM, we use the next (neglected) timescale as an # estimate of the decorrelation time if msm_nr.nstates > self.nstates: corrtime = max(1, msm_nr.timescales()[self.nstates - 1]) # use the smaller of these two pessimistic estimates self.stride = int(min(self.lag, 2 * corrtime)) # if stride is different to init_hmsm, check if microstates in lagged-strided trajs are compatible if self.stride != self.init_hmsm.stride: dtrajs_lagged_strided = _lag_observations(dtrajs, self.lag, stride=self.stride) _nstates_obs = _number_of_states(dtrajs_lagged_strided, only_used=True) _nstates_obs_full = _number_of_states(dtrajs) if _np.setxor1d(_np.concatenate(dtrajs_lagged_strided), _np.concatenate(self.init_hmsm._dtrajs_lagged)).size != 0: raise UserWarning('Choice of stride has excluded a different set of microstates than in ' + 'init_hmsm. Set of observed microstates in time-lagged strided trajectories ' + 'must match to the one used for init_hmsm estimation.') self._dtrajs_full = dtrajs self._dtrajs_lagged = dtrajs_lagged_strided self._nstates_obs_full = _nstates_obs_full self._nstates_obs = _nstates_obs self._observable_set = _np.arange(self._nstates_obs) self._dtrajs_obs = dtrajs else: copy_attributes += ['_dtrajs_full', '_dtrajs_lagged', '_nstates_obs_full', '_nstates_obs', '_observable_set', '_dtrajs_obs'] # update self with estimates from init_hmsm self.__dict__.update( {k: i for k, i in self.init_hmsm.__dict__.items() if k in copy_attributes}) # as mentioned in the docstring, take init_hmsm observed set observation probabilities self.observe_nonempty = False # update HMM Model self.update_model_params(P=self.init_hmsm.transition_matrix, pobs=self.init_hmsm.observation_probabilities, dt_model=TimeUnit(self.dt_traj).get_scaled(self.lag)) # check if we have a valid initial model import msmtools.estimation as msmest if self.reversible and not msmest.is_connected(self.count_matrix): raise NotImplementedError('Encountered disconnected count matrix:\n ' + str(self.count_matrix) + 'with reversible Bayesian HMM sampler using lag=' + str(self.lag) + ' and stride=' + str(self.stride) + '. Consider using shorter lag, ' + 'or shorter stride (to use more of the data), ' + 'or using a lower value for mincount_connectivity.') # here we blow up the output matrix (if needed) to the FULL state space because we want to use dtrajs in the # Bayesian HMM sampler. This is just an initialization. nstates_full = msmest.number_of_states(dtrajs) if self.nstates_obs < nstates_full: eps = 0.01 / nstates_full # default output probability, in order to avoid zero columns # full state space output matrix. make sure there are no zero columns B_init = eps * _np.ones((self.nstates, nstates_full), dtype=_np.float64) # fill active states B_init[:, self.observable_set] = _np.maximum(eps, self.observation_probabilities) # renormalize B to make it row-stochastic B_init /= B_init.sum(axis=1)[:, None] else: B_init = self.observation_probabilities # HMM sampler if self.show_progress: self._progress_register(self.nsamples, description='Sampling HMSMs', stage=0) def call_back(): self._progress_update(1, stage=0) else: call_back = None from bhmm import discrete_hmm, bayesian_hmm if self.init_hmsm is not None: hmm_mle = self.init_hmsm.hmm else: hmm_mle = discrete_hmm(self.initial_distribution, self.transition_matrix, B_init) sampled_hmm = bayesian_hmm(self.discrete_trajectories_lagged, hmm_mle, nsample=self.nsamples, reversible=self.reversible, stationary=self.stationary, p0_prior=self.p0_prior, transition_matrix_prior=self.transition_matrix_prior, store_hidden=self.store_hidden, call_back=call_back) if self.show_progress: self._progress_force_finish(stage=0) # Samples sample_inp = [(m.transition_matrix, m.stationary_distribution, m.output_probabilities) for m in sampled_hmm.sampled_hmms] samples = [] for P, pi, pobs in sample_inp: # restrict to observable set if necessary Bobs = pobs[:, self.observable_set] pobs = Bobs / Bobs.sum(axis=1)[:, None] # renormalize samples.append(_HMSM(P, pobs, pi=pi, dt_model=self.dt_model)) # store results self.sampled_trajs = [sampled_hmm.sampled_hmms[i].hidden_state_trajectories for i in range(self.nsamples)] self.update_model_params(samples=samples) # deal with connectivity states_subset = None if self.connectivity == 'largest': states_subset = 'largest-strong' elif self.connectivity == 'populous': states_subset = 'populous-strong' # OBSERVATION SET if self.observe_nonempty: observe_subset = 'nonempty' else: observe_subset = None # return submodel (will return self if all None) return self.submodel(states=states_subset, obs=observe_subset, mincount_connectivity=self.mincount_connectivity)
def dt_traj(self, value): # time step self._dt_traj = value from pyemma.util.units import TimeUnit self.timestep_traj = TimeUnit(self.dt_traj)
def __init__(self, nstates=2, lag=1, stride=1, msm_init=None, reversible=True, connectivity='largest', observe_active=True, dt_traj='1 step', accuracy=1e-3, maxit=1000): r"""Maximum likelihood estimator for a Hidden MSM given a MSM Parameters ---------- nstates : int, optional, default=2 number of hidden states lag : int, optional, default=1 lagtime to estimate the HMSM at stride : str or int, default=1 stride between two lagged trajectories extracted from the input trajectories. Given trajectory s[t], stride and lag will result in trajectories s[0], s[lag], s[2 lag], ... s[stride], s[stride + lag], s[stride + 2 lag], ... Setting stride = 1 will result in using all data (useful for maximum likelihood estimator), while a Bayesian estimator requires a longer stride in order to have statistically uncorrelated trajectories. Setting stride = 'effective' uses the largest neglected timescale as an estimate for the correlation time and sets the stride accordingly msm_init : :class:`MSM <pyemma.msm.estimators.msm_estimated.MSM>` MSM object to initialize the estimation reversible : bool, optional, default = True If true compute reversible MSM, else non-reversible MSM connectivity : str, optional, default = 'largest' Connectivity mode. Three methods are intended (currently only 'largest' is implemented) * 'largest' : The active set is the largest reversibly connected set. All estimation will be done on this subset and all quantities (transition matrix, stationary distribution, etc) are only defined on this subset and are correspondingly smaller than the full set of states * 'all' : The active set is the full set of states. Estimation will be conducted on each reversibly connected set separately. That means the transition matrix will decompose into disconnected submatrices, the stationary vector is only defined within subsets, etc. Currently not implemented. * 'none' : The active set is the full set of states. Estimation will be conducted on the full set of states without ensuring connectivity. This only permits nonreversible estimation. Currently not implemented. observe_active : bool, optional, default=True True: Restricts the observation set to the active states of the MSM. False: All states are in the observation set. dt_traj : str, optional, default='1 step' Description of the physical time corresponding to the trajectory time step. May be used by analysis algorithms such as plotting tools to pretty-print the axes. By default '1 step', i.e. there is no physical time unit. Specify by a number, whitespace and unit. Permitted units are (* is an arbitrary string): | 'fs', 'femtosecond*' | 'ps', 'picosecond*' | 'ns', 'nanosecond*' | 'us', 'microsecond*' | 'ms', 'millisecond*' | 's', 'second*' accuracy : float, optional, default = 1e-3 convergence threshold for EM iteration. When two the likelihood does not increase by more than accuracy, the iteration is stopped successfully. maxit : int, optional, default = 1000 stopping criterion for EM iteration. When so many iterations are performed without reaching the requested accuracy, the iteration is stopped without convergence (a warning is given) """ self.nstates = nstates self.lag = lag self.stride = stride self.msm_init = msm_init self.reversible = reversible self.connectivity = connectivity self.observe_active = observe_active self.dt_traj = dt_traj self.timestep_traj = TimeUnit(dt_traj) self.accuracy = accuracy self.maxit = maxit
def _dt_model(self, msm): from pyemma.util.units import TimeUnit tu = TimeUnit("1 step").get_scaled(self.msm.lag) self.assertEqual(msm.dt_model, tu)
def __init__(self, nstates=2, lag=1, stride=1, msm_init='largest-strong', reversible=True, stationary=False, connectivity=None, mincount_connectivity='1/n', observe_nonempty=True, separate=None, dt_traj='1 step', accuracy=1e-3, maxit=1000): r"""Maximum likelihood estimator for a Hidden MSM given a MSM Parameters ---------- nstates : int, optional, default=2 number of hidden states lag : int, optional, default=1 lagtime to estimate the HMSM at stride : str or int, default=1 stride between two lagged trajectories extracted from the input trajectories. Given trajectory s[t], stride and lag will result in trajectories s[0], s[lag], s[2 lag], ... s[stride], s[stride + lag], s[stride + 2 lag], ... Setting stride = 1 will result in using all data (useful for maximum likelihood estimator), while a Bayesian estimator requires a longer stride in order to have statistically uncorrelated trajectories. Setting stride = 'effective' uses the largest neglected timescale as an estimate for the correlation time and sets the stride accordingly msm_init : str or :class:`MSM <pyemma.msm.MSM>` MSM object to initialize the estimation, or one of following keywords: * 'largest-strong' or None (default) : Estimate MSM on the largest strongly connected set and use spectral clustering to generate an initial HMM * 'all' : Estimate MSM(s) on the full state space to initialize the HMM. This estimate maybe weakly connected or disconnected. reversible : bool, optional, default = True If true compute reversible MSM, else non-reversible MSM stationary : bool, optional, default=False If True, the initial distribution of hidden states is self-consistently computed as the stationary distribution of the transition matrix. If False, it will be estimated from the starting states. Only set this to true if you're sure that the observation trajectories are initiated from a global equilibrium distribution. connectivity : str, optional, default = None Defines if the resulting HMM will be defined on all hidden states or on a connected subset. Connectivity is defined by counting only transitions with at least mincount_connectivity counts. If a subset of states is used, all estimated quantities (transition matrix, stationary distribution, etc) are only defined on this subset and are correspondingly smaller than nstates. Following modes are available: * None or 'all' : The active set is the full set of states. Estimation is done on all weakly connected subsets separately. The resulting transition matrix may be disconnected. * 'largest' : The active set is the largest reversibly connected set. * 'populous' : The active set is the reversibly connected set with most counts. mincount_connectivity : float or '1/n' minimum number of counts to consider a connection between two states. Counts lower than that will count zero in the connectivity check and may thus separate the resulting transition matrix. The default evaluates to 1/nstates. separate : None or iterable of int Force the given set of observed states to stay in a separate hidden state. The remaining nstates-1 states will be assigned by a metastable decomposition. observe_nonempty : bool If True, will restricted the observed states to the states that have at least one observation in the lagged input trajectories. If an initial MSM is given, this option is ignored and the observed subset is always identical to the active set of that MSM. dt_traj : str, optional, default='1 step' Description of the physical time corresponding to the trajectory time step. May be used by analysis algorithms such as plotting tools to pretty-print the axes. By default '1 step', i.e. there is no physical time unit. Specify by a number, whitespace and unit. Permitted units are (* is an arbitrary string): | 'fs', 'femtosecond*' | 'ps', 'picosecond*' | 'ns', 'nanosecond*' | 'us', 'microsecond*' | 'ms', 'millisecond*' | 's', 'second*' accuracy : float, optional, default = 1e-3 convergence threshold for EM iteration. When two the likelihood does not increase by more than accuracy, the iteration is stopped successfully. maxit : int, optional, default = 1000 stopping criterion for EM iteration. When so many iterations are performed without reaching the requested accuracy, the iteration is stopped without convergence (a warning is given) """ self.nstates = nstates self.lag = lag self.stride = stride self.msm_init = msm_init self.reversible = reversible self.stationary = stationary self.connectivity = connectivity if mincount_connectivity == '1/n': mincount_connectivity = 1.0/float(nstates) self.mincount_connectivity = mincount_connectivity self.separate = separate self.observe_nonempty = observe_nonempty self.dt_traj = dt_traj self.timestep_traj = TimeUnit(dt_traj) self.accuracy = accuracy self.maxit = maxit
def dt_model(self, value): self._dt_model = value from pyemma.util.units import TimeUnit # this is used internally to scale output times to a physical time unit. self._timeunit_model = TimeUnit(self.dt_model)
def __init__(self, dtrajs, dt_traj, lag, connectivity, active_set, connected_sets, C_full, C_active, transition_matrix): r"""Estimates a Markov model from discrete trajectories. Parameters ---------- dtrajs : list containing ndarrays(dtype=int) or ndarray(n, dtype=int) discrete trajectories, stored as integer ndarrays (arbitrary size) or a single ndarray for only one trajectory. dt_traj : str, optional, default='1 step' Description of the physical time corresponding to the trajectory time step. May be used by analysis algorithms such as plotting tools to pretty-print the axes. By default '1 step', i.e. there is no physical time unit. Specify by a number, whitespace and unit. Permitted units are (* is an arbitrary string): | 'fs', 'femtosecond*' | 'ps', 'picosecond*' | 'ns', 'nanosecond*' | 'us', 'microsecond*' | 'ms', 'millisecond*' | 's', 'second*' lagtime : int lagtime for the MSM estimation in multiples of trajectory steps connectivity : str, optional, default = 'largest' Connectivity mode. Three methods are intended (currently only 'largest' is implemented) * 'largest' : The active set is the largest reversibly connected set. All estimation will be done on this subset and all quantities (transition matrix, stationary distribution, etc) are only defined on this subset and are correspondingly smaller than the full set of states * 'all' : The active set is the full set of states. Estimation will be conducted on each reversibly connected set separately. That means the transition matrix will decompose into disconnected submatrices, the stationary vector is only defined within subsets, etc. Currently not implemented. * 'none' : The active set is the full set of states. Estimation will be conducted on the full set of states without ensuring connectivity. This only permits nonreversible estimation. Currently not implemented. active_set : connected_sets : C_full : C_active : transition_matrix : """ # superclass constructor MSM.__init__(self, transition_matrix, dt_model=TimeUnit(dt_traj).get_scaled(lag)) # Making copies because we don't know what will happen to the arguments after this call self.lag = lag self.connectivity = copy.deepcopy(connectivity) self.active_set = copy.deepcopy(active_set) self._dtrajs_full = copy.deepcopy(dtrajs) self.dt_traj = dt_traj self._C_full = copy.deepcopy(C_full) self._C_active = copy.deepcopy(C_active) self._connected_sets = copy.deepcopy(connected_sets) # calculate secondary quantities self._nstates_full = np.shape(C_full)[0] # full2active mapping self._full2active = -1 * np.ones(self._nstates_full, dtype=int) self._full2active[self._active_set] = np.array(list( range(len(self._active_set))), dtype=int) # is estimated self._is_estimated = True