def createXi( xi, n, maxiter = 9999 ): ''' Xi = createXi( xi, n ) In: n is an integer xi is a probability distribution on the integers (must be able to calculate the probabilities of multiple integers at once), OR xi is a vector (list, numpy.ndarray, ...) such that sum(xi) = 1, maxiter is an integer (default maxiter = 999) Out: Xi is a random vector such that Xi[i] ~ xi for all i and sum(Xi) = n - 1. The program crashes if the number of trials exceeds maxiter. The algorithm is described in 'Simulating size-constrained Galton-Watson trees' by Luc Devroye. Note that the algorithm does not necessarily ever stop if the expected value of xi is far from 1! ''' # Check if xi is a probability distribution, # or else a vector of probabilities. if hasattr(xi,'__call__'): Multinom = lambda m,zeta: graphUtil.multinomial( m, zeta ) else: Multinom = lambda m,P: np.random.multinomial( m, P ) # Multinomial method, page 8. trials = 0 while True: N = Multinom( n, xi ) K = len(N) trials += 1 # If sum( j*N[j] ) == n-1, then we are done. # If not, then we try again. if ( np.arange(K) * N ).sum() == n-1: break if trials >= maxiter: raise RuntimeError('Maximum number of trials reached' +\ ' (%d). ' % maxiter +\ 'Try again or choose another' +\ ' probability distribution.') print '\t%d trials' % trials # Create a vector with N[j] copies of j # for j = 0, ..., K-1 and permute it randomly. Xi = np.concatenate([ j * np.ones( N[j] ) for j in range(K) ]) Xi = np.random.permutation( Xi ) # Random walk, page 5. S = 1 # S_0 minS = 1 index = 0 for t in range( n - 1 ): # S_t = S_(t-1) + Xi[t] - 1 # minS_t = min{r <= t: S_r} = S_index S = S + Xi[t+1] - 1 # S_(t+1) if S < minS: minS = S #minS_(t+1) index = t + 1 # Rotate Xi such that the random walk ends at 0. Xi = np.roll( Xi, -index - 1 ) return Xi.astype( int )
def labelMobileRand( M, odd, parent ): ''' labelMobileRand( M, odd, parent ) In: M is a mobile. odd and parent are vertices in M. {odd,parent} is an edge in M. odd is of an odd generation (and thus, parent is of even generation). parent has already been given a label. Out: All descendants of odd of even generation have been given a random label. ''' # The children to be labeled. children = graphUtil.childrenOf( odd, M, parent ) # Make sure that the children get the labels in the correct order. children.sort() n = len(children) if n == 0: return # Make use of the multinomial method to get labels # such that the differences of nearby labels are # i.i.d. # Maximum number of trials: maxiter = 999 # Probability distribution of jumps + 1: xi = lambda k: 0.5 ** ( k + 1. ) trials = 0 while True: N = graphUtil.multinomial( n + 1, xi ) K = len(N) trials += 1 # If sum( (j-1)*N[j] ) == 0, then we are done. # If not, try again. if ( ( np.arange(K) - 1 ) * N ).sum() == 0: break if trials >= maxiter: raise RuntimeError('Maximum number of trials reached' +\ ' (%d). ' % maxiter +\ 'Try again or choose another' +\ 'probability distribution.') # Create a vector with N[j] copies of j # for j = 0, ..., K-1 and permute it randomly. Jumps = np.concatenate([ ( j - 1 ) * np.ones( N[j] ) for j in range(K) ]) Jumps = np.random.permutation( Jumps ).astype( int ) # Now, Jumps is a vector of n + 1 independent random variables # distributed by f(k) = 0.5^(k+2) for k >= -1 and sum(Jumps) == 0. # The labels are determined by the label of odd's parent. currentLabel = M.node[parent][ 'label' ] # Label odd's children. for i in range(len( children )): v = children[i] currentLabel = currentLabel + Jumps[i] M.node[v][ 'label' ] = currentLabel # Find all children of v (they are of an odd generation) # and label them. childrenOf_v = graphUtil.childrenOf( v, M, odd ) for child_v in childrenOf_v: labelMobileRand( M, child_v, v )
def createXi(xi, n, maxiter=9999): ''' Xi = createXi( xi, n ) In: n is an integer xi is a probability distribution on the integers (must be able to calculate the probabilities of multiple integers at once), OR xi is a vector (list, numpy.ndarray, ...) such that sum(xi) = 1, maxiter is an integer (default maxiter = 999) Out: Xi is a random vector such that Xi[i] ~ xi for all i and sum(Xi) = n - 1. The program crashes if the number of trials exceeds maxiter. The algorithm is described in 'Simulating size-constrained Galton-Watson trees' by Luc Devroye. Note that the algorithm does not necessarily ever stop if the expected value of xi is far from 1! ''' # Check if xi is a probability distribution, # or else a vector of probabilities. if hasattr(xi, '__call__'): Multinom = lambda m, zeta: graphUtil.multinomial(m, zeta) else: Multinom = lambda m, P: np.random.multinomial(m, P) # Multinomial method, page 8. trials = 0 while True: N = Multinom(n, xi) K = len(N) trials += 1 # If sum( j*N[j] ) == n-1, then we are done. # If not, then we try again. if (np.arange(K) * N).sum() == n - 1: break if trials >= maxiter: raise RuntimeError('Maximum number of trials reached' +\ ' (%d). ' % maxiter +\ 'Try again or choose another' +\ ' probability distribution.') print '\t%d trials' % trials # Create a vector with N[j] copies of j # for j = 0, ..., K-1 and permute it randomly. Xi = np.concatenate([j * np.ones(N[j]) for j in range(K)]) Xi = np.random.permutation(Xi) # Random walk, page 5. S = 1 # S_0 minS = 1 index = 0 for t in range(n - 1): # S_t = S_(t-1) + Xi[t] - 1 # minS_t = min{r <= t: S_r} = S_index S = S + Xi[t + 1] - 1 # S_(t+1) if S < minS: minS = S #minS_(t+1) index = t + 1 # Rotate Xi such that the random walk ends at 0. Xi = np.roll(Xi, -index - 1) return Xi.astype(int)