def sample(self, prior: MarkovStateModel, n_samples: int, n_steps: Optional[int] = None, callback=None): r""" Performs sampling based on a prior. Parameters ---------- prior : MarkovStateModel The MSM that is used as initial sampling point. n_samples : int The number of samples to draw. n_steps : int, optional, default=None The number of sampling steps for each transition matrix. If None, determined by :math:`\sqrt{\mathrm{n\_states}}`. callback : callable, optional, default=None Callback function that indicates progress of sampling. Returns ------- samples : list of :obj:`MarkovStateModel` The generated samples Examples -------- This method can in particular be used to append samples to an already estimated posterior: >>> import numpy as np >>> import deeptime as dt >>> dtrajs = [np.array([0,1,2,2,2,2,1,2,2,2,1,0,0,0,0,0,0,0]), ... np.array([0,0,0,0,1,1,2,2,2,2,2,2,2,1,0,0])] >>> prior = dt.markov.msm.MaximumLikelihoodMSM().fit(dtrajs, lagtime=1) >>> estimator = dt.markov.msm.BayesianMSM() >>> posterior = estimator.fit(prior).fetch_model() >>> n_samples = len(posterior.samples) >>> posterior.samples.extend(estimator.sample(posterior.prior, n_samples=23)) >>> assert len(posterior.samples) == n_samples + 23 """ if n_steps is None: # heuristic for number of steps to decorrelate n_steps = int(sqrt(prior.count_model.n_states_full)) # transition matrix sampler from deeptime.markov.tools.estimation import tmatrix_sampler if self.stationary_distribution_constraint is None: tsampler = tmatrix_sampler(prior.count_model.count_matrix, reversible=self.reversible, T0=prior.transition_matrix, nsteps=n_steps) else: # Use the stationary distribution on the active set of states statdist_active = prior.stationary_distribution # We can not use the MLE as T0. Use the initialization in the reversible pi sampler tsampler = tmatrix_sampler(prior.count_model.count_matrix, reversible=self.reversible, mu=statdist_active, nsteps=n_steps) sample_Ps, sample_mus = tsampler.sample(nsamples=n_samples, return_statdist=True, callback=callback) # construct sampled MSMs samples = [ MarkovStateModel(P, stationary_distribution=pi, reversible=self.reversible, count_model=prior.count_model, transition_matrix_tolerance=prior.transition_matrix_tolerance) for P, pi in zip(sample_Ps, sample_mus) ] return samples
def fit_from_msm(self, msm: MarkovStateModel, callback=None): r""" Fits a bayesian posterior from a given Markov state model. The MSM must contain a count model to be able to produce confidences. Note that the count model should be produced using effective counting, otherwise counts are correlated and computed confidences are wrong. Parameters ---------- msm : MarkovStateModel The Markov state model to use as sampling start point. callback : callable, optional, default=None Function to be called to indicate progress of sampling. Returns ------- self : BayesianMSM Reference to self. """ if not msm.has_count_model: raise ValueError( "Can only sample confidences with a count model. The counting mode should be 'effective'" " to avoid correlations between counts and therefore wrong confidences." ) # transition matrix sampler from deeptime.markov.tools.estimation import tmatrix_sampler from math import sqrt if self.n_steps is None: # heuristic for number of steps to decorrelate self.n_steps = int(sqrt(msm.count_model.n_states_full)) # use the same count matrix as the MLE. This is why we have effective as a default if self.stationary_distribution_constraint is None: tsampler = tmatrix_sampler(msm.count_model.count_matrix, reversible=self.reversible, T0=msm.transition_matrix, nsteps=self.n_steps) else: # Use the stationary distribution on the active set of states statdist_active = msm.stationary_distribution # We can not use the MLE as T0. Use the initialization in the reversible pi sampler tsampler = tmatrix_sampler(msm.count_model.count_matrix, reversible=self.reversible, mu=statdist_active, nsteps=self.n_steps) sample_Ps, sample_mus = tsampler.sample(nsamples=self.n_samples, return_statdist=True, call_back=callback) # construct sampled MSMs samples = [ MarkovStateModel( P, stationary_distribution=pi, reversible=self.reversible, count_model=msm.count_model, transition_matrix_tolerance=msm.transition_matrix_tolerance) for P, pi in zip(sample_Ps, sample_mus) ] self._model = BayesianPosterior(prior=msm, samples=samples) return self
def _estimate(self, dtrajs): if self.core_set is not None and self.count_mode == 'effective': raise RuntimeError( 'Cannot estimate core set MSM with effective counting.') # conduct MLE estimation (superclass) first _MLMSM._estimate(self, dtrajs) # transition matrix sampler from math import sqrt if self.nsteps is None: self.nsteps = int(sqrt( self.nstates)) # heuristic for number of steps to decorrelate # use the same count matrix as the MLE. This is why we have effective as a default if self.statdist_constraint is None: tsampler = tmatrix_sampler(self.count_matrix_active, reversible=self.reversible, T0=self.transition_matrix, nsteps=self.nsteps) else: # Use the stationary distribution on the active set of states statdist_active = self.pi # We can not use the MLE as T0. Use the initialization in the reversible pi sampler tsampler = tmatrix_sampler(self.count_matrix_active, reversible=self.reversible, mu=statdist_active, nsteps=self.nsteps) if self.show_progress: #and self.nstates >= 1000: self._progress_register(self.nsamples, '{}: Sampling MSMs'.format(self.name), stage=0) call_back = lambda: self._progress_update(1) else: call_back = None with self._progress_context(stage='all'): sample_Ps, sample_mus = tsampler.sample(nsamples=self.nsamples, return_statdist=True, callback=call_back) # construct sampled MSMs samples = [] for P, pi in zip(sample_Ps, sample_mus): samples.append( _MSM(P, pi=pi, reversible=self.reversible, dt_model=self.dt_model)) # update self model self.update_model_params(samples=samples) # done return self
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])
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)
def test_revpi(self): N = self.N sampler = tmatrix_sampler(self.C, reversible=True, mu=self.pi) M = self.C.shape[0] T_sample = np.zeros((N, M, M)) for i in range(N): T_sample[i, :, :] = sampler.sample() H, xed = np.histogram(T_sample[:, 0, 1], self.xedges) P_sampled = 1.0 * H / self.N P_analytical = self.probabilities_revpi(self.xedges) self.assertTrue(np.all(np.abs(P_sampled - P_analytical) < 0.02))
def test_rev(self): N = self.N sampler = tmatrix_sampler(self.C, reversible=True) M = self.C.shape[0] T_sample = np.zeros((N, M, M)) for i in range(N): T_sample[i, :, :] = sampler.sample() p_12 = T_sample[:, 0, 1] p_21 = T_sample[:, 1, 0] H, xed, yed = np.histogram2d(p_12, p_21, bins=(self.xedges, self.yedges)) P_sampled = H / self.N P_analytical = self.probabilities_rev(self.xedges, self.yedges) self.assertTrue(np.all(np.abs(P_sampled - P_analytical) < 0.01))