def iterate_distributions(self,indices,k): """ Iterate a number of distributions k steps from their last iteration. This is the core method for iterating distributions to stationarity. It will automatically set the stationary distribution if it is previously unknown and reached during the iteration. 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,:]) # did we find the stationary distribution? # determine this by checking the defintion of stationarity # selecting the number of iterations and the threshold is a numerical task if not(self.stationary_distribution_known()): for i in indices: last = self.get_last_iteration(i) last_iterated = mkm.iterate_distributions(self.p,last,100) # 1e-6 is a good threshold? if mkm.relative_error(last,last_iterated) < 1e-6: self.sd = last
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 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 assert_iteration(self,indices,t): """ For the distributions given by indices, assert that there exists an iteration at time t. indices: list with indices of distributions t: time """ for i in indices: t0 = 0 for t1 in self.get_iteration_times(i): if t1 > t0 and t1 <= t: t0 = t1 if t0 != t: y = mkm.iterate_distributions(self.p,self.get_iteration(i,t0),t-t0) self.add_iteration(i,t,y)
def refine_iterations(self, indices, refine): """ Refine the iterations (that is add iterations in between existing iterations) until they meet a certain criterion. indices: list containing indices of distributions to be iterated refine: boolean function of two iterations where a return value of 'True' means that an additional iteration in between the given iterations is necessary (i.e. refine(t1, iteration1, t2, iteration2)) """ import time print "INFO: Refining " + ` len( indices ) ` + " distribution(s) for a Markov chain with n=" + ` self.get_n( ) ` + "." print "INFO: For multiple distributions, refinement might take longer than iterating to stationarity." # Iterating distributions one after another for i in indices: t = 0 while t != self.last_iteration_time(i): next_t = self.next_iteration_time(i, t) while t + 1 != next_t and refine(t, self.get_iteration( i, t), next_t, self.get_iteration(i, next_t)) == True: k = int((next_t - t) / 2) start = time.time() y = mkm.iterate_distributions(self.p, self.get_iteration(i, t), k) seconds = time.time() - start self.add_iteration(i, t + k, y) next_t = self.next_iteration_time(i, t) t = next_t return print "INFO: Done."
def assert_iteration(self, indices, t): """ For the distributions given by indices, assert that there exists an iteration at time t. indices: list with indices of distributions t: time """ for i in indices: t0 = 0 for t1 in self.get_iteration_times(i): if t1 > t0 and t1 <= t: t0 = t1 if t0 != t: y = mkm.iterate_distributions(self.p, self.get_iteration(i, t0), t - t0) self.add_iteration(i, t, y)
def refine_iterations(self,indices,refine): """ Refine the iterations (that is add iterations in between existing iterations) until they meet a certain criterion. indices: list containing indices of distributions to be iterated refine: boolean function of two iterations where a return value of 'True' means that an additional iteration in between the given iterations is necessary (i.e. refine(t1, iteration1, t2, iteration2)) """ import time print "INFO: Refining "+`len(indices)`+" distribution(s) for a Markov chain with n="+`self.get_n()`+"." print "INFO: For multiple distributions, refinement might take longer than iterating to stationarity." # Iterating distributions one after another for i in indices: t = 0 while t != self.last_iteration_time(i): next_t = self.next_iteration_time(i,t) while t+1 != next_t and refine(t, self.get_iteration(i,t), next_t, self.get_iteration(i,next_t)) == True: k = int((next_t-t)/2) start = time.time() y = mkm.iterate_distributions(self.p,self.get_iteration(i,t),k) seconds = time.time() - start self.add_iteration(i,t+k,y) next_t = self.next_iteration_time(i,t) t = next_t return print "INFO: Done."
def refine_iterations(self,indices,refine): """ Refine the iterations (that is add iterations in between existing iterations) until they meet a certain criterion. indices: list containing indices of distributions to be iterated refine: boolean function of two iterations where a return value of 'True' means that an additional iteration in between the given iterations is necessary """ import time print "INFO: Refining "+`len(indices)`+" distribution(s) for a Markov chain with n="+`self.get_n()`+"." print "INFO: Iterating distributions one after another." for i in indices: t = 0 while t != self.last_iteration_time(i): next_t = self.next_iteration_time(i,t) while t+1 != next_t and refine(self.get_iteration(i,t), self.get_iteration(i,next_t)) == True: k = int((next_t-t)/2) start = time.time() y = mkm.iterate_distributions(self.p,self.get_iteration(i,t),k) seconds = time.time() - start print time.strftime("%d %b %H:%M", time.localtime())+": "+`k`+" iteration step(s) completed (that took %(sec).2f seconds)." % {'sec': seconds} self.add_iteration(i,t+k,y) next_t = self.next_iteration_time(i,t) t = next_t return
def test_iteration(): import numpy, time, random N = 10000 k = 10000 P = mkm.line_lazy_transition_matrix(N) P = P.transpose() P = P.tocsr() # single distribution x = mkm.delta_distribution(N,0) start = time.time() for i in xrange(k): x = P.dot(x) end = time.time() print "Python loop:" print end - start print x x = mkm.delta_distribution(N,0) start = time.time() x = mkm.matrix_vector_iteration_local(P,x,k) end = time.time() print "Python local iteration:" print end - start print x x = mkm.delta_distribution(N,0) start = time.time() x = mkm.matrix_vector_iteration_by_processes(P,x,k) end = time.time() print "Python iterating (multiple processes):" print end - start print x P = P.transpose() x = mkm.delta_distribution(N,0) start = time.time() x = mkm.iterate_distributions(P,x,k) end = time.time() print "Generic Python iteration:" print end - start print x P = P.transpose() # multiple distributions k = 10000 nd = 10 random.seed(0) x = mkm.random_delta_distributions(N,nd).transpose() start = time.time() x = mkm.matrix_vector_iteration_local(P,x,k) end = time.time() print "Python local iteration:" print end - start print x random.seed(0) x = mkm.random_delta_distributions(N,nd).transpose() start = time.time() x = mkm.matrix_vector_iteration_by_processes(P,x,k) end = time.time() print "Python iterating (multiple processes):" print end - start print x random.seed(0) P = P.transpose() x = mkm.random_delta_distributions(N,nd) start = time.time() x = mkm.iterate_distributions(P,x,k).transpose() end = time.time() print "Generic Python iteration:" print end - start print x P = P.transpose()