Performance on Random Number generation is intolerable. Whenever you need many deviates from the same distribution but with different parameters it takes forever.
Here is a thread on Poisson deviates being slow. The comments and answers are all aimed at Poisson Deviates and do not address the fundamental shortcoming Mathematica has generating random numbers.
Here I asked why MultiNomial Deviates were so slow, and didn't get an answer, although one person suggested there was no solution.
Elsewhere I asked how to generate BinomialDeviates and got a helpful Binomial-specific answer.
The problem is I don't want to spend hours trying to work around Mathematica's intolerable performance each time I need some deviates from a standard distribution.
This is essentially a suggestion that WRI needs to add function calls for people who need large numbers of random deviates in which all the parameters are different. Below I show the syntax for such a call for binomial variates in R, Octave, and scipy.
R:
num<-10^5
n<-sample(1:10,num,TRUE)
p<-runif(num)
ptm <- proc.time()
out=rbinom(num,n,p)
proc.time()-ptm
Here is the same thing in Octave:
num=10^7;
n=randi([1,10],num,1);
p=rand(num,1);
t=cputime;
out=binornd(n,p);
cputime-t
and scipy:
num=10**7
n=stats.randint.rvs(1,10,size=num)
p=stats.uniform.rvs(0,1,num)
out=stats.binom.rvs(n,p)
NOTE: The point is not to generate the distribution of deviates that are obtained for
uniformly distributed p
and n
. The point is to generate a bunch of deviates when each deviate is drawn for a different parameter value. The manner in which the parameters are obtained should be considered as unknown. I used uniform for p
and n
because they had to be set to something, not because I am interested in the resulting distribution.
As you can see the calls in R, Octave, aand Python (scipy) where the actual work desired is performed is a single line of self-evident code in each case. The performance of all these languages blows Mathematica away. Mathematica has Solve
and NSolve
, DSolve
& NDSolve
, Integrate
& NIntegrate
, Sum
and NSum
, Product
& NProduct
... Something similar (to the numerical versions) should be implemented for random number generation.
Below there are 3 answers. blochwave's solution has the best performance and makes calls to C++ routines which the user must write. The routine poissonvariate.cpp has 48 lines. In addition there are two lines of Mathematica which must be written as well. The 48 lines of cpp could obviously be copied from the working poissonvariate and then morphed into the code needed for the desired generator but one must still perform substantially more work to implement this solution for new generators than one must in R, Octave, and python. My solution was to use Rlink and generate the numbers in R. THis gave a performance improvement of about 100 over native Mathematica which for my purposes was adequate. The amount of code required to implement the R solution is 2 + n where n is the number of distinct random variate types you need. (BY "type" I mean, exponential, binomial, poisson, multinomial, beta, chisquare, etc. etc. etc.) . I believe that most users value both code simplicity and performance. The 3rd answer did not provide a solution to the problem that was posed.
Below I compare computing 10,000 random deviates in Mathematica against 10,000,000 in C. I draw from 5 distributions in Mathematica and C: Binomial, Exponential, Chi-Squared, Poisson and Gamma. In C I generated about 50 million deviates in about 10 seconds. In Mathematica I generated about 50,000 deviates in about 8 seconds. If I generate 10,000 deviates of each distribution in C the code runs in about 0.03 seconds which is roughly 250 times faster than Mathematica. Is there really no way to improve this?
Here is the comparison between Mathematica and C. The upshot is that there is no comparison. C is about 100-1000 times faster.
num = 10000
ints = RandomInteger[{1, 10}, num];
reals = RandomReal[{0, 1}, num];
mus = RandomReal[{0, 1}, num];
mf = Thread[BinomialDistribution[ints, reals]];
gf = Thread[GammaDistribution[mus, reals]];
RandomVariate /@ mf; // Timing
RandomVariate /@ gf; // Timing
RandomVariate[PoissonDistribution[#]] & /@ mus; // Timing
RandomVariate[ChiSquareDistribution[#]] & /@ ints; // Timing
RandomVariate[ExponentialDistribution[#]] & /@ mus; // Timing
The 10,000 Binomial deviates take 6 seconds (on a MacBook Air) to generate. The Poisson deviates take over 1 second.
The C code below uses the library ranlib
, which is documented and virtually bullet-proof as far as I know. You can get ranlib.c
here - it's the 7th library down.
To run the code below save it to a file called ran_test.c
. Get the source code from the library and put linpack.c
, ranlib.c
, com.c
and ranlib.h
into the same directory. On a Unix machine (e.g. Linux or Mac) that has GCC installed type the following to compile.
gcc -O2 ran_test.c linpack.c ranlib.c com.c -o tst
To time and execute the code enter: time ./tst
on the command line and hit Return. On my MacBook Air the C generates 10 million deviates from each of the following distributions: Gamma, Binomial, Chi-Squared, Exponential and Poisson. The parameters for the distributions are different on each call. The code runs in about 10 seconds. Thus it is roughly 1,000 times faster than Mathematica.
#include <stdio.h>
#include <stdlib.h>
#include "ranlib.h"
#define MILLION 1000000
#define TEN_MILLION 10000000
main(){
long int i,j,k,dev,count, ix,n;
long iseed1=100,iseed2=1000;
long int ncat, rint;
float p, x,pp;
float mu;
setall(iseed1,iseed2);
rint = ignuin(1,TEN_MILLION);
printf("random int=%ld\n",rint);
for(k=0;k<TEN_MILLION;k++){
p=genunf(0,1);
ncat=ignuin(1,10);
n=ignbin(ncat, p);
mu=genunf(0,10000);
x=gengam(genunf(0,100),genunf(0,10000));
/* print a few of the deviates */
if(k%rint==0)printf("gamma deviate=%g\n",x);
if(k%rint==0)printf("binomial deviate=%ld\n",n);
n=ignpoi(mu);
if(k%rint==0)printf("poisson variate n=%ld\n",n);
p=genexp(mu);
if(k%rint==0)printf("exp variate =%g\n",p);
p=genchi(mu);
if(k%rint==0)printf("chi square =%g\n",p);
if(k%rint==0)printf("\n");
}
}
num, ints, reals, mus, f, mf, rvs, gf
... just to do a timing test? Talking of efficient, who has time to look through all that? $\endgroup$