/* * Copyright (c) 2002-2006 Samit Basu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "Core.hpp" #include "Exception.hpp" #include "Malloc.hpp" #include #include #include "RanLib.hpp" static bool initialized = false; //! //@Module SEED Seed the Random Number Generator //@@Section RANDOM //@@Usage //Seeds the random number generator using the given integer seeds. //Changing the seed allows you to choose which pseudo-random //sequence is generated. The seed takes two @|uint32| values: //@[ // seed(s,t) //@] //where @|s| and @|t| are the seed values. Note that due to limitations //in @|ranlib|, the values of @|s,t| must be between @|0 <= s,t <= 2^30|. //@@Example //Here's an example of how the seed value can be used to reproduce //a specific random number sequence. //@< //seed(32,41); //rand(1,5) //seed(32,41); //rand(1,5) //@> //! ArrayVector SeedFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("Seed function requires a two integer arguments"); Array tmp1(arg[0]); uint32 seedval1; Array tmp2(arg[1]); uint32 seedval2; seedval1 = tmp1.getContentsAsIntegerScalar(); seedval2 = tmp2.getContentsAsIntegerScalar(); setall(seedval1,seedval2); init_genrand(seedval1); initialized = true; return ArrayVector(); } //! //@Module RANDBETA Beta Deviate Random Number Generator //@@Section RANDOM //@@Usage //Creates an array of beta random deviates based on the supplied //two parameters. The general syntax for @|randbeta| is //@[ // y = randbeta(alpha, beta) //@] //where @|alpha| and @|beta| are the two parameters of the //random deviate. There are three forms for calling @|randbeta|. //The first uses two vectors @|alpha| and @|beta| of the same //size, in which case the output @|y| is the same size as both //inputs, and each deviate uses the corresponding values of @|alpha| //and @|beta| from the arguments. In the other forms, either //@|alpha| or @|beta| are scalars. //@@Function Internals //The probability density function (PDF) of a beta random variable //is //\[ //f(x) = x^(a-1) * (1-x)^(b-1) / B(a,b) //\] //for @|x| between 0 and 1. The function @|B(a,b)| is defined so //that the integral of @|f(x)| is 1. //@@Example //Here is a plot of the PDF of a beta random variable with @|a=3|, //@|b=7|. //@< //a = 3; b = 7; //x = (0:100)/100; t = x.^(a-1).*(1-x).^(b-1); //t = t/(sum(t)*.01); //plot(x,t); //mprint betapdf //@> //which is plotted as //@figure betapdf //If we generate a few random deviates with these values, //we see they are distributed around the peak of roughly //@|0.25|. //@< //randbeta(3*ones(1,5),7*ones(1,5)) //@> //! ArrayVector RandBetaFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randbeta requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randbeta requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_FLOAT); arg2.promoteType(FM_FLOAT); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); int i; for (i=0;i //! ArrayVector RandIFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randi requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randbeta requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_INT32); arg2.promoteType(FM_INT32); int32 *dp; dp = (int32 *) Malloc(sizeof(int32)*outDims.getElementCount()); int32 *p1; p1 = (int32*) arg1.getDataPointer(); int32 *p2; p2 = (int32*) arg2.getDataPointer(); int i; for (i=0;i //The PDF is below: //@figure chipdf //Here is an example of using @|randchi| and @|randn| to compute //some chi-square random variables with four degrees of freedom. //@< //randchi(4*ones(1,6)) //sum(randn(4,6).^2) //@> //! ArrayVector RandChiFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 1) throw Exception("randchi requires exactly one parameter (the vector of degrees of freedom)"); Array arg1(arg[0]); if (arg1.isEmpty()) return singleArrayVector(Array::emptyConstructor()); arg1.promoteType(FM_FLOAT); // Output dimension is the larger of the two Dimensions outDims(arg1.dimensions()); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); int i; for (i=0;i //! ArrayVector RandExpFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 1) throw Exception("randexp requires exactly one parameter (the vector of means)"); Array arg1(arg[0]); if (arg1.isEmpty()) return singleArrayVector(Array::emptyConstructor()); arg1.promoteType(FM_FLOAT); // Output dimension is the larger of the two Dimensions outDims(arg1.dimensions()); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); int i; for (i=0;i //! ArrayVector RandPoissonFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 1) throw Exception("randp requires exactly one parameter (the vector of means)"); Array arg1(arg[0]); if (arg1.isEmpty()) return singleArrayVector(Array::emptyConstructor()); arg1.promoteType(FM_FLOAT); // Output dimension is the larger of the two Dimensions outDims(arg1.dimensions()); int32 *dp; dp = (int32 *) Malloc(sizeof(int32)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); int i; for (i=0;i //! ArrayVector RandBinFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randbin requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randbin requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_UINT32); arg2.promoteType(FM_FLOAT); uint32 *dp; dp = (uint32 *) Malloc(sizeof(uint32)*outDims.getElementCount()); uint32 *p1; p1 = (uint32*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); int i; for (i=0;i //! ArrayVector RandNBinFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randnbin requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randnbin requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_UINT32); arg2.promoteType(FM_FLOAT); uint32 *dp; dp = (uint32 *) Malloc(sizeof(uint32)*outDims.getElementCount()); uint32 *p1; p1 = (uint32*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); int i; for (i=0;i //! ArrayVector RandFFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randf requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randf requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_FLOAT); arg2.promoteType(FM_FLOAT); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); int i; for (i=0;i //! ArrayVector RandGammaFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randgamma requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randgamma requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_FLOAT); arg2.promoteType(FM_FLOAT); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); int i; for (i=0;i //! ArrayVector RandMultiFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randmulti requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); int N = arg1.getContentsAsIntegerScalar(); if (N<0) throw Exception("number of events to generate for randmulti must be a nonnegative integer"); arg2.promoteType(FM_FLOAT); // Verify the correctness of the probability argument float *dp; dp = (float*) arg2.getReadWriteDataPointer(); float Psum = 0.0; int i; for (i=0;i 1)) throw Exception("probabiliy vector argument to randmulti must have all elements between 0 and 1"); Psum += dp[i]; } for (i=0;i= 1), //and @|mu| is the non-centrality shift (must be positive). //@@Function Internals //A non-central chi-square random variable is the sum of a chisquare //deviate with @|n-1| degrees of freedom plus the square of a normal //deviate with mean @|mu| and standard deviation 1. //@@Examples //Here is an example of a non-central chi-square random variable: //@< //randnchi(5*ones(1,9),0.3) //@> //! ArrayVector RandNChiFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 2) throw Exception("randnchi requires two parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); if (arg1.isEmpty() || arg2.isEmpty()) return singleArrayVector(Array::emptyConstructor()); // Check the logic to see if one or both are scalar values if (!(arg1.isScalar() || arg2.isScalar() || (arg1.dimensions().equals(arg2.dimensions())))) throw Exception("randnchi requires either one of the two arguments to be a scalar, or both arguments to be the same size"); int arg1_advance; int arg2_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; // Output dimension is the larger of the two Dimensions outDims; if (arg1.getLength() > arg2.getLength()) { outDims = arg1.dimensions(); } else { outDims = arg2.dimensions(); } arg1.promoteType(FM_FLOAT); arg2.promoteType(FM_FLOAT); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); int i; for (i=0;i 1.0"); if (p2[i*arg2_advance] < 0.0) throw Exception("noncentrality parameter must be positive"); dp[i] = gennch(p1[i*arg1_advance],p2[i*arg2_advance]); } return singleArrayVector(Array(FM_FLOAT,outDims,dp)); } //! //@Module RANDNF Generate Noncentral F-Distribution Random Variable //@@Section RANDOM //@@Usage //Generates a vector of non-central F-distributed random variables //with the specified parameters. The general syntax for its use is //@[ // y = randnf(n,m,c) //@] //where @|n| is the number of degrees of freedom in the numerator, //and @|m| is the number of degrees of freedom in the denominator. //The vector @|c| determines the non-centrality shift of the numerator. //@@Function Internals //A non-central F-distributed random variable is the ratio of a //non-central chi-square random variable and a central chi-square random //variable, i.e., //\[ // F_{n,m,c} = \frac{\chi_{n,c}^2/n}{\chi_m^2/m}. //\] //@@Example //Here we use the @|randf| to generate some non-central F-distributed //random variables: //@< //randnf(5*ones(1,9),7,1.34) //@> //! ArrayVector RandNFFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 3) throw Exception("randnf requires three parameter arguments"); Array arg1(arg[0]); Array arg2(arg[1]); Array arg3(arg[2]); if (arg1.isEmpty() || arg2.isEmpty() || arg3.isEmpty()) return singleArrayVector(Array::emptyConstructor()); int arg1_advance; int arg2_advance; int arg3_advance; arg1_advance = (arg1.isScalar()) ? 0 : 1; arg2_advance = (arg2.isScalar()) ? 0 : 1; arg3_advance = (arg3.isScalar()) ? 0 : 1; if (arg1_advance && arg2_advance && (!arg1.dimensions().equals(arg2.dimensions()))) throw Exception("vector arguments to randnf must be the same size"); if (arg1_advance && arg3_advance && (!arg1.dimensions().equals(arg3.dimensions()))) throw Exception("vector arguments to randnf must be the same size"); if (arg2_advance && arg3_advance && (!arg2.dimensions().equals(arg3.dimensions()))) throw Exception("vector arguments to randnf must be the same size"); // Output dimension is the larger of the two Dimensions outDims; if ((arg1.getLength() > arg2.getLength()) && (arg1.getLength() > arg3.getLength())) outDims = arg1.dimensions(); else if ((arg2.getLength() > arg1.getLength()) && (arg2.getLength() > arg3.getLength())) outDims = arg2.dimensions(); else if ((arg3.getLength() > arg1.getLength()) && (arg3.getLength() > arg2.getLength())) outDims = arg3.dimensions(); arg1.promoteType(FM_FLOAT); arg2.promoteType(FM_FLOAT); arg3.promoteType(FM_FLOAT); float *dp; dp = (float *) Malloc(sizeof(float)*outDims.getElementCount()); float *p1; p1 = (float*) arg1.getDataPointer(); float *p2; p2 = (float*) arg2.getDataPointer(); float *p3; p3 = (float*) arg3.getDataPointer(); int i; for (i=0;i 1.0"); if (p2[i*arg2_advance] <= 0.0) throw Exception("denominator degrees of freedom must be positive"); if (p3[i*arg2_advance] < 0.0) throw Exception("noncentrality parameter must be non-negative"); dp[i] = gennf(p1[i*arg1_advance],p2[i*arg2_advance],p3[i*arg3_advance]); } return singleArrayVector(Array(FM_FLOAT,outDims,dp)); } void InitializeRandGen() { unsigned long init[4]={0x923, 0x234, 0x405, 0x456}, length=4; init_by_array(init, length); initialized = true; } ArrayVector RandStateControl(const ArrayVector& arg) { string key = arg[0].getContentsAsStringUpper(); if (!(key=="STATE")) throw Exception("expecting string 'state' as first argument"); if (arg.size() == 1) { uint32 *mt = (uint32*) Malloc(sizeof(uint32)*625); GetRandStateVect(mt); return singleArrayVector(Array(FM_UINT32,Dimensions(625,1),mt)); } else { Array statevec(arg[1]); if ((statevec.isScalar()) && (statevec.getContentsAsIntegerScalar() == 0)) { InitializeRandGen(); return ArrayVector(); } else { statevec.promoteType(FM_UINT32); if (statevec.getLength() != 625) throw Exception("illegal state vector - must be of length 625"); SetRandStateVect((uint32*)statevec.getDataPointer()); return ArrayVector(); } } } //! //@Module RANDN Gaussian (Normal) Random Number Generator //@@Section RANDOM //@@Usage //Creates an array of pseudo-random numbers of the specified size. //The numbers are normally distributed with zero mean and a unit //standard deviation (i.e., @|mu = 0, sigma = 1|). // Two seperate syntaxes are possible. The first syntax specifies the array //dimensions as a sequence of scalar dimensions: //@[ // y = randn(d1,d2,...,dn). //@] //The resulting array has the given dimensions, and is filled with //random numbers. The type of @|y| is @|double|, a 64-bit floating //point array. To get arrays of other types, use the typecast //functions. // //The second syntax specifies the array dimensions as a vector, //where each element in the vector specifies a dimension length: //@[ // y = randn([d1,d2,...,dn]). //@] //This syntax is more convenient for calling @|randn| using a //variable for the argument. // //Finally, @|randn| supports two additional forms that allow //you to manipulate the state of the random number generator. //The first retrieves the state //@[ // y = randn('state') //@] //which is a 625 length integer vector. The second form sets //the state //@[ // randn('state',y) //@] //or alternately, you can reset the random number generator with //@[ // randn('state',0) //@] //@@Function Internals //Recall that the //probability density function (PDF) of a normal random variable is //\[ //f(x) = \frac{1}{\sqrt{2\pi \sigma^2}} e^{\frac{-(x-\mu)^2}{2\sigma^2}}. //\] //The Gaussian random numbers are generated from pairs of uniform random numbers using a transformation technique. //@@Example //The following example demonstrates an example of using the first form of the @|randn| function. //@< //randn(2,2,2) //@> //The second example demonstrates the second form of the @|randn| function. //@< //randn([2,2,2]) //@> //In the next example, we create a large array of 10000 normally distributed pseudo-random numbers. We then shift the mean to 10, and the variance to 5. We then numerically calculate the mean and variance using @|mean| and @|var|, respectively. //@< //x = 10+sqrt(5)*randn(1,10000); //mean(x) //var(x) //@> //Now, we use the state manipulation functions of @|randn| to exactly reproduce //a random sequence. Note that unlike using @|seed|, we can exactly control where //the random number generator starts by saving the state. //@< //randn('state',0) % restores us to startup conditions //a = randn(1,3) % random sequence 1 //b = randn('state'); % capture the state vector //c = randn(1,3) % random sequence 2 //randn('state',b); % restart the random generator so... //c = randn(1,3) % we get random sequence 2 again //@> //! ArrayVector RandnFunction(int nargout, const ArrayVector& arg) { if (!initialized) InitializeRandGen(); int i; Array t; Dimensions dims; int32 *dp; if (arg.size() == 0) dims.makeScalar(); else { if (arg[0].isString()) return RandStateControl(arg); // Case 1 - all of the entries are scalar bool allScalars; allScalars = true; for (i=0;i 1) throw Exception("Arguments to randn function must be either all scalars or a single vector"); t = arg[0]; t.promoteType(FM_UINT32); dp = (int*) t.getDataPointer(); for (i=0;i= 0); if (!allPositive) throw Exception("Randn function requires positive arguments"); } double *qp; qp = (double*) Malloc(sizeof(double)*dims.getElementCount()); int len; int j; len = dims.getElementCount(); for (j=0;j= 1.0 ); w = sqrt( (-2.0 * log( w ) ) / w ); y1 = x1 * w; y2 = x2 * w; qp[j] = y1; if (j //The second example demonstrates the second form of the @|rand| function. //@< //rand([2,2,2]) //@> //The third example computes the mean and variance of a large number of uniform random numbers. Recall that the mean should be @|1/2|, and the variance should be @|1/12 ~ 0.083|. //@< //x = rand(1,10000); //mean(x) //var(x) //@> //Now, we use the state manipulation functions of @|rand| to exactly reproduce //a random sequence. Note that unlike using @|seed|, we can exactly control where //the random number generator starts by saving the state. //@< //rand('state',0) % restores us to startup conditions //a = rand(1,3) % random sequence 1 //b = rand('state'); % capture the state vector //c = rand(1,3) % random sequence 2 //rand('state',b); % restart the random generator so... //c = rand(1,3) % we get random sequence 2 again //@> //! ArrayVector RandFunction(int nargout, const ArrayVector& arg) { if (!initialized) InitializeRandGen(); int i; Array t; Dimensions dims; int32 *dp; if (arg.size() == 0) dims.makeScalar(); else { // Check for state assignment if (arg[0].isString()) return RandStateControl(arg); // Case 1 - all of the entries are scalar bool allScalars; allScalars = true; for (i=0;i 1) throw Exception("Arguments to rand function must be either all scalars or a single vector"); t = arg[0]; t.promoteType(FM_UINT32); dp = (int*) t.getDataPointer(); for (i=0;i= 0); if (!allPositive) throw Exception("Rand function requires posItive arguments"); } double *qp; qp = (double*) Malloc(sizeof(double)*dims.getElementCount()); int len; int j; len = dims.getElementCount(); for (j=0;j