def compute_tv_mixing(self, indices=None, convergence_tol=0.05, refinement_tol=0.1): """ Compute the mixing in total variation for a number of distributions. Automatically iterates the distributions to stationarity if necessary. indices: list containing indices of distributions for which to find the total variation mixing. Defaults to None (for all distributions). convergence_tol: tolerance in total variation distance for the determination of convergence. Defaults to 0.05. refinement_tol: maximum distance in total variation between two iterations. If the mixing is plotted, this corresponds to the smoothness of the graph. Defaults to 0.1. Returns nothing. """ if indices == None: indices = range(self.num_distributions()) self.iterate_distributions_to_stationarity(indices, tv_tol=convergence_tol) # want subsequent iterations to have a maximal difference of refinement_tol units # of total variation (as compared to the stationary distribution) self.refine_iterations( indices, lambda t1, x1, t2, x2: abs( mkm.total_variation(x1, self.get_stationary()) - mkm. total_variation(x2, self.get_stationary())) > refinement_tol)
def distribution_tv_mixing(self,index): if self.sd == None: raise Exception('cant determinde the mixing as long as the stationary distribution is unknown') x = [] tv = [] for idx,t in enumerate(self.get_iteration_times(index)): x.append(t) tv.append(mkm.total_variation(self.sd,self.get_iteration(index,t))) return (x,tv)
def iterate_distributions(self, indices, k, tv_tol=0.05): """ Iterate a number of distributions k steps from their last iteration. This is the core method for iterating distributions to stationarity. If the number of distributions is >= 3, it will automatically set the stationary distribution, if unknown and found. indices: list containing indices of distributions to be iterated k: number of iterations to perform """ x = [] for i in indices: x.append(self.get_last_iteration(i)) # invoke the external method performing the iteration y = mkm.iterate_distributions(self.p, numpy.array(x), k) for idx, val in enumerate(indices): self.add_iteration(val, self.last_iteration_time(val) + k, y[idx, :]) # check if we found the stationary distribution if len(indices) >= 3: # first check the definition of stationarity for all iterations for i in indices: last = self.get_last_iteration(i) last_iterated = mkm.iterate_distributions(self.p, last, 100) # is 1e-6 a good threshold? (however this can never be trusted -> cutoff) if mkm.relative_error(last, last_iterated) > 1e-6: return # check pairwise distance <= tol for i in indices: d_i = self.get_last_iteration(i) for j in indices: d_j = self.get_last_iteration(j) if mkm.total_variation(d_i, d_j) > tv_tol: return # take the arithmetic mean of all iterations as the stationary distribution stationary_candidate = numpy.zeros(self.n) for i in indices: stationary_candidate = stationary_candidate + self.get_last_iteration( i) stationary_candidate = stationary_candidate / len(indices) self.set_stationary(stationary_candidate)
def close_to_stationarity(self, dist, tv_tol=0.05): """ For a distribution dist, determine if it is "close" to the stationary distribution. Currently implemented as total variation distance < tv_tol. tv_tol: Desired tolerance in total variation. Defaults to 0.05. """ if self.sd == None: return False if mkm.total_variation(dist, self.sd) < tv_tol: return True
def close_to_stationarity(self,dist,tv_tol=0.05): """ For a distribution dist, determine if it is "close" to the stationary distribution. Currently implemented as total variation distance < tv_tol. tv_tol: Desired tolerance in total variation. Defaults to 0.05. """ if self.sd == None: return False if mkm.total_variation(dist,self.sd) < tv_tol: return True
def convergence_video(self, path, index, seconds): """ """ import matplotlib.pyplot as plt nframes = 100 * seconds # first iterate the distribution to stationarity (if that has not been done already) self.iterate_distributions_to_stationarity([index]) # we want the video to end once the stationary distribution is reached t_end = self.last_iteration_time(index) for t in self.get_iteration_times(index): if mkm.total_variation(self.get_iteration(index, t), self.get_stationary()) < 0.01: t_end = int(t * 1.05) break frametime = t_end / float(nframes) # if possible, we want an iteration for every frame self.refine_iterations( [index], lambda t1, x1, t2, x2: (t1 <= t_end or t2 <= t_end) and abs(t1 - t2) > frametime) def frame(i): fig = plt.figure(figsize=(19.20, 10.80), dpi=100) # time of closest iteration t = self.closest_iteration_time(index, i * frametime) iteration = self.get_iteration(index, t) mkm.pyplot_bar(iteration) plt.title("Probability distribution after %d steps" % (t)) plt.xlabel("Markov chain state space") plt.ylabel("Probabiliy") plt.tick_params(axis='x', which='both', bottom='off', top='off', labelbottom='off') plt.xlim(0, self.n) plt.ylim(0, 1.1 * numpy.max(iteration)) return fig mkm.matplotlib_plots_to_video(path, frame, nframes)
def iterate_distributions(self,indices,k,tv_tol=0.05): """ Iterate a number of distributions k steps from their last iteration. This is the core method for iterating distributions to stationarity. If the number of distributions is >= 3, it will automatically set the stationary distribution, if unknown and found. indices: list containing indices of distributions to be iterated k: number of iterations to perform """ x = [] for i in indices: x.append(self.get_last_iteration(i)) # invoke the external method performing the iteration y = mkm.iterate_distributions(self.p,numpy.array(x),k) for idx, val in enumerate(indices): self.add_iteration(val,self.last_iteration_time(val)+k,y[idx,:]) # check if we found the stationary distribution if len(indices) >= 3: # first check the definition of stationarity for all iterations for i in indices: last = self.get_last_iteration(i) last_iterated = mkm.iterate_distributions(self.p,last,100) # is 1e-6 a good threshold? (however this can never be trusted -> cutoff) if mkm.relative_error(last,last_iterated) > 1e-6: return # check pairwise distance <= tol for i in indices: d_i = self.get_last_iteration(i) for j in indices: d_j = self.get_last_iteration(j) if mkm.total_variation(d_i, d_j) > tv_tol: return # take the arithmetic mean of all iterations as the stationary distribution stationary_candidate = numpy.zeros(self.n) for i in indices: stationary_candidate = stationary_candidate + self.get_last_iteration(i) stationary_candidate = stationary_candidate / len(indices) self.set_stationary(stationary_candidate)
def distribution_tv_mixing(self,index): """ Returns a tupel (t,tv) that contains the distance in total variation to stationarity for the given distribution at all known times t. index: index of the distribution """ x = [] tv = [] for idx,t in enumerate(self.get_iteration_times(index)): x.append(t) tv.append(mkm.total_variation(self.sd,self.get_iteration(index,t))) return (x,tv)
def distribution_tv_mixing(self, index): """ Returns a tupel (t,tv) that contains the distance in total variation to stationarity for the given distribution at all known times t. index: index of the distribution """ x = [] tv = [] for idx, t in enumerate(self.get_iteration_times(index)): x.append(t) tv.append( mkm.total_variation(self.sd, self.get_iteration(index, t))) return (x, tv)
def convergence_video(self,path,index,seconds): """ """ import matplotlib.pyplot as plt nframes = 100*seconds # first iterate the distribution to stationarity (if that has not been done already) self.iterate_distributions_to_stationarity([index]) # we want the video to end once the stationary distribution is reached t_end = self.last_iteration_time(index) for t in self.get_iteration_times(index): if mkm.total_variation(self.get_iteration(index,t), self.get_stationary()) < 0.01: t_end = int(t*1.05) break frametime = t_end/float(nframes) # if possible, we want an iteration for every frame self.refine_iterations([index], lambda t1,x1,t2,x2: (t1<=t_end or t2<=t_end) and abs(t1-t2) > frametime) def frame(i): fig = plt.figure(figsize=(19.20, 10.80), dpi=100) # time of closest iteration t = self.closest_iteration_time(index,i*frametime) iteration = self.get_iteration(index,t) mkm.pyplot_bar(iteration) plt.title("Probability distribution after %d steps" % (t)) plt.xlabel("Markov chain state space") plt.ylabel("Probabiliy") plt.tick_params(axis='x', which='both', bottom='off', top='off', labelbottom='off') plt.xlim(0, self.n) plt.ylim(0, 1.1*numpy.max(iteration)) return fig mkm.matplotlib_plots_to_video(path, frame, nframes)
def compute_tv_mixing(self,indices=None): """ Compute the mixing in total variation for a number of distributions. Automatically iterates the distributions to stationarity if necessary. indices: list containing indices of distributions for which to find the total variation mixing. Defaults to None (for all distributions). Returns nothing. """ if indices == None: indices = range(self.num_distributions()) self.iterate_distributions_to_stationarity(indices) # want subsequent iterations to have a maximal difference of 0.1 units # of total variation (as compared to the stationary distribution) self.refine_iterations(indices, lambda x,y: abs(mkm.total_variation(x,self.get_stationary_distribution()) - mkm.total_variation(y,self.get_stationary_distribution())) > 0.1 )
def compute_tv_mixing(self,indices=None,convergence_tol=0.05,refinement_tol=0.1): """ Compute the mixing in total variation for a number of distributions. Automatically iterates the distributions to stationarity if necessary. indices: list containing indices of distributions for which to find the total variation mixing. Defaults to None (for all distributions). convergence_tol: tolerance in total variation distance for the determination of convergence. Defaults to 0.05. refinement_tol: maximum distance in total variation between two iterations. If the mixing is plotted, this corresponds to the smoothness of the graph. Defaults to 0.1. Returns nothing. """ if indices == None: indices = range(self.num_distributions()) self.iterate_distributions_to_stationarity(indices, tv_tol=convergence_tol) # want subsequent iterations to have a maximal difference of refinement_tol units # of total variation (as compared to the stationary distribution) self.refine_iterations(indices, lambda t1,x1,t2,x2: abs(mkm.total_variation(x1,self.get_stationary()) - mkm.total_variation(x2,self.get_stationary())) > refinement_tol)
def plot_tv_mixing(self, indices=None, y_tol=0.1, threshold=0.05, text=True): """ Plots the total variation mixing for a given number of distributions. Iterates the distributions to stationarity if necessary. The x-limit of the plot will be choosen such that the total varation distance to stationarity of all distributions is below threshold. indices: list with indices of distributions for which to plot the mixing. Can also be an integer, indicating a single distribution. Defaults to None (for all distributions). y_tol: maximum y-distance between to data points on the same graph threshold: determines the x-limit of the plot. Defaults to 0.05. text: if the plot should contain axis labels and a title """ import matplotlib.pyplot as plt if indices == None: indices = range(self.num_distributions()) if isinstance(indices, int): indices = [indices] # iterate distributions to stationarity, given the desired threshold self.compute_tv_mixing(indices, convergence_tol=threshold, refinement_tol=y_tol) # determine the x-limit of the plot xlim = 5 for index in indices: for t in self.get_iteration_times(index): if t > xlim and mkm.total_variation( self.sd, self.get_iteration(index, t)) > threshold: xlim = t self.assert_iteration(indices, xlim) self.compute_tv_mixing(indices, convergence_tol=threshold, refinement_tol=y_tol) # need the refinement # plot for index in indices: x = [] tv = [] for t in self.get_iteration_times(index): if t > xlim: continue x.append(t) tv.append( mkm.total_variation(self.sd, self.get_iteration(index, t))) plt.plot(x, tv) plt.xlim(0, xlim) plt.ylim(0, 1) if text: plt.xlabel("t") plt.ylabel("Total variation distance to stationarity") plt.show()
def plot_tv_mixing(self,indices=None,y_tol=0.1,threshold=0.05,text=True): """ Plots the total variation mixing for a given number of distributions. Iterates the distributions to stationarity if necessary. The x-limit of the plot will be choosen such that the total varation distance to stationarity of all distributions is below threshold. indices: list with indices of distributions for which to plot the mixing. Can also be an integer, indicating a single distribution. Defaults to None (for all distributions). y_tol: maximum y-distance between to data points on the same graph threshold: determines the x-limit of the plot. Defaults to 0.05. text: if the plot should contain axis labels and a title """ import matplotlib.pyplot as plt if indices == None: indices = range(self.num_distributions()) if isinstance(indices, int): indices = [indices] # iterate distributions to stationarity, given the desired threshold self.compute_tv_mixing(indices, convergence_tol=threshold, refinement_tol=y_tol) # determine the x-limit of the plot xlim = 5 for index in indices: for t in self.get_iteration_times(index): if t > xlim and mkm.total_variation(self.sd,self.get_iteration(index,t)) > threshold: xlim = t self.assert_iteration(indices, xlim) self.compute_tv_mixing(indices, convergence_tol=threshold, refinement_tol=y_tol) # need the refinement # plot for index in indices: x = [] tv = [] for t in self.get_iteration_times(index): if t > xlim: continue x.append(t) tv.append(mkm.total_variation(self.sd,self.get_iteration(index,t))) plt.plot(x, tv) plt.xlim(0, xlim) plt.ylim(0, 1) if text: plt.xlabel("t") plt.ylabel("Total variation distance to stationarity") plt.show()
def close_to_stationarity(self,dist): if self.sd == None: return False if mkm.total_variation(dist,self.sd) < 0.05: return True