import ranlib2 as ranlib import numarray.numeric as num import numarray.linear_algebra as linalg import sys import math import types as _types import numarray.generic as _gen __doc__ = """Random number array generators. This module provides functions to generate numarray of random numbers. """ # Extended RandomArray to provide more distributions: # normal, beta, chi square, F, multivariate normal, # exponential, binomial, multinomial # Lee Barford, Dec. 1999. # Extended most functions to accept arrays or sequences as # arguments, with broadcasting when necessary. # Peter J. Verveer, 2003. ArgumentError = "ArgumentError" def seed(x=0,y=0): """Set the RNG seed using the integers x, y; If |y| == 0 set a random one from clock. """ if type (x) != _types.IntType or type (y) != _types.IntType : raise ArgumentError, "seed requires integer arguments." if y == 0: import time t = time.time()*10**4 ndigits = int(math.log10(t)) base = 10**(ndigits/2) x = int(t/base) y = 1 + int(t%base) ranlib.set_seeds(x,y) seed() def get_seed(): """Return the current seed pair""" return ranlib.get_seeds() def _broadcast_arguments(args, shape): """If there are no arrays/sequences in the args tuple, return args and shape unchanged. Otherwise: if shape is empty, broadcast all arrays/sequences in the arguments to a common shape, and return the result together with the broadcasted shape. If shape is not empty, broadcast all arrays/sequences in the arguments to the given shape and return these together with the unchanged shape. Scalar arguments are never changed. """ if isinstance(shape, _types.IntType): shape = [shape] array_args = [] for arg in args: if (isinstance(arg, num.ArrayType) or isinstance(arg, _types.ListType) or isinstance(arg, _types.TupleType)): array_args.append(num.array(arg)) if len(array_args) > 0: if len(shape) == 0: array_args = _gen._nWayBroadcast(array_args) shape = array_args[0].shape else: for ii in range(len(array_args)): array_args[ii] = _gen._broadcast(num.array(array_args[ii]), shape) new_args = [] for arg in args: if (isinstance(arg, num.ArrayType) or isinstance(arg, _types.ListType) or isinstance(arg, _types.TupleType)): new_args.append(array_args.pop(0)) else: new_args.append(arg) args = tuple(new_args) return args, shape def _build_random_array(fun, args, shape=[]): """Build an array by applying function |fun| to the arguments in |args|, creating an array with the specified shape. Allows an integer shape n as a shorthand for (n,). """ args, shape = _broadcast_arguments(args, shape) if len(shape) != 0: n = num.multiply.reduce(shape) s = apply(fun, args + (n,)) s.setshape(shape) return s else: n = 1 s = apply(fun, args + (n,)) return s[0] def random(shape=[]): """random(n) or random([n, m, ...]) Returns array of random numbers in the range 0.0 to 1.0. """ return _build_random_array(ranlib.sample, (), shape) def uniform(minimum, maximum, shape=[]): """uniform([minimum,], maximum[, shape]) Return array with shape |shape| of random Floats with all values > minimum and < maximum. """ (minimum, maximum), shape = _broadcast_arguments((minimum, maximum), shape) return minimum + (maximum-minimum)*random(shape) def randint(minimum, maximum=None, shape=[]): """randint([minimum,] maximum[, shape]) Return random integers >= mininimum, < maximum; if only one parameter specified, return random integers >= 0, < maximum. """ if maximum is None: maximum, minimum = minimum, 0 a = num.floor(uniform(minimum, maximum, shape)) if isinstance(a, num.ArrayType): return a.astype(num.Int32) else: return int(a) def random_integers(maximum, minimum=1, shape=[]): """Return array of random integers in interval [minimum, maximum] Note that this function has reversed arguments. It is simply a redirection through randint, and use of that function (randint) is suggested. """ return randint(minimum, maximum+1, shape) def permutation(n): """A permutation of indices range(n)""" return num.argsort(random(n)) def standard_normal(shape=[]): """standard_normal(n) or standard_normal([n, m, ...]) Returns array of random numbers normally distributed with mean 0 and standard deviation 1. """ return _build_random_array(ranlib.standard_normal, (), shape) def normal(mean, std, shape=[]): """normal(mean, std, n) or normal(mean, std, [n, m, ...]) Returns array of random numbers randomly distributed with specified mean and standard deviation """ (mean, std), shape = _broadcast_arguments((mean, std), shape) s = standard_normal(shape) return s * std + mean def multivariate_normal(mean, cov, shape=[]): """multivariate_normal(mean, cov) or multivariate_normal(mean, cov, [m, n, ...]) Returns an array containing multivariate normally distributed random numbers with specified mean and covariance. |mean| must be a one-dimensional array. |cov| must be a square two-dimensional array with the same number of rows and columns as |mean| has elements. The first form returns a single 1-D array containing a multivariate normal. The second form returns an array of shape (m, n, ..., cov.getshape()[0]). In this case, output[i,j,...,:] is a 1-D array containing a multivariate normal. """ # Check preconditions on arguments mean = num.array(mean) cov = num.array(cov) if len(mean.getshape()) != 1: raise ArgumentError, "mean must be 1 dimensional." if (len(cov.getshape()) != 2) or (cov.getshape()[0] != cov.getshape()[1]): raise ArgumentError, "cov must be 2 dimensional and square." if mean.getshape()[0] != cov.getshape()[0]: raise ArgumentError, "mean and cov must have same length." # Compute shape of output if isinstance(shape, _types.IntType): shape = [shape] final_shape = list(shape[:]) final_shape.append(mean.getshape()[0]) # Create a matrix of independent standard normally distributed random # numbers. The matrix has rows with the same length as mean and as # many rows are necessary to form a matrix of shape final_shape. x = ranlib.standard_normal(num.multiply.reduce(final_shape)) x.setshape(num.multiply.reduce(final_shape[0:len(final_shape)-1]), mean.getshape()[0]) # Transform matrix of standard normals into matrix where each row # contains multivariate normals with the desired covariance. # Compute A such that matrixmultiply(transpose(A),A) == cov. # Then the matrix products of the rows of x and A has the desired # covariance. Note that sqrt(s)*v where (u,s,v) is the singular value # decomposition of cov is such an A. (u,s,v) = linalg.singular_value_decomposition(cov) x = num.matrixmultiply(x*num.sqrt(s),v) # The rows of x now have the correct covariance but mean 0. Add # mean to each row. Then each row will have mean mean. num.add(mean,x,x) x.setshape(final_shape) return x def exponential(mean, shape=[]): """exponential(mean, n) or exponential(mean, [n, m, ...]) Returns array of random numbers exponentially distributed with specified mean """ # If U is a random number uniformly distributed on [0,1], then # -ln(U) is exponentially distributed with mean 1, and so # -ln(U)*M is exponentially distributed with mean M. (mean,), shape = _broadcast_arguments((mean,), shape) x = random(shape) num.log(x, x) num.subtract(0.0, x, x) num.multiply(mean, x, x) return x def beta(a, b, shape=[]): """beta(a, b) or beta(a, b, [n, m, ...]) returns array of beta distributed random numbers.""" return _build_random_array(ranlib.beta, (a, b), shape) def gamma(a, r, shape=[]): """gamma(a, r) or gamma(a, r, [n, m, ...]) returns array of gamma distributed random numbers.""" return _build_random_array(ranlib.gamma, (a, r), shape) def F(dfn, dfd, shape=[]): """F(dfn, dfd) or F(dfn, dfd, [n, m, ...]) Returns array of F distributed random numbers with dfn degrees of freedom in the numerator and dfd degrees of freedom in the denominator. """ (dfn, dfd), shape = _broadcast_arguments((dfn, dfd), shape) return ( chi_square(dfn, shape) / dfn) / ( chi_square(dfd, shape) / dfd) def noncentral_F(dfn, dfd, nconc, shape=[]): """noncentral_F(dfn, dfd, nonc) or noncentral_F(dfn, dfd, nonc, [n, m, ...]) Returns array of noncentral F distributed random numbers with dfn degrees of freedom in the numerator and dfd degrees of freedom in the denominator, and noncentrality parameter nconc. """ (dfn, dfd, nconc), shape = _broadcast_arguments((dfn, dfd, nconc), shape) return ( noncentral_chi_square(dfn, nconc, shape) / dfn ) / ( chi_square(dfd, shape) /dfd ) def chi_square(df, shape=[]): """chi_square(df) or chi_square(df, [n, m, ...]) Returns array of chi squared distributed random numbers with df degrees of freedom. """ return _build_random_array(ranlib.chisquare, (df,), shape) def noncentral_chi_square(df, nconc, shape=[]): """noncentral_chi_square(df, nconc) or chi_square(df, nconc, [n, m, ...]) Returns array of noncentral chi squared distributed random numbers with df degrees of freedom and noncentrality parameter. """ return _build_random_array(ranlib.noncentral_chisquare, (df, nconc), shape) def binomial(trials, p, shape=[]): """binomial(trials, p) or binomial(trials, p, [n, m, ...]) Returns array of binomially distributed random integers. |trials| is the number of trials in the binomial distribution. |p| is the probability of an event in each trial of the binomial distribution. """ return _build_random_array(ranlib.binomial, (trials, p), shape) def negative_binomial(trials, p, shape=[]): """negative_binomial(trials, p) or negative_binomial(trials, p, [n, m, ...]) Returns array of negative binomially distributed random integers. |trials| is the number of trials in the negative binomial distribution. |p| is the probability of an event in each trial of the negative binomial distribution. """ return _build_random_array(ranlib.negative_binomial, (trials, p), shape) def multinomial(trials, probs, shape=[]): """multinomial(trials, probs) or multinomial(trials, probs, [n, m, ...]) Returns array of multinomial distributed integer vectors. |trials| is the number of trials in each multinomial distribution. |probs| is a one dimensional array. There are len(prob)+1 events. prob[i] is the probability of the i-th event, 0<=i