samer@0
|
1 /*
|
samer@0
|
2 * Pseudorandom generators for SWI Prolog
|
samer@0
|
3 * Samer Abdallah (2009)
|
samer@0
|
4 * Some samplers adapted from code in Tom Minka's lightspeed Matlab toolbox.
|
samer@0
|
5 * Other bits of code culled from SWI Prolog distribution random.c
|
samer@0
|
6 *
|
samer@0
|
7 * To do:
|
samer@0
|
8 * work out lower bound for skew stable distributions
|
samer@0
|
9 * stream splitting with jumps
|
samer@0
|
10 * reduce blob copying
|
samer@0
|
11 * fast discrete distributions
|
samer@0
|
12 * ziggurat for normals
|
samer@0
|
13 */
|
samer@0
|
14
|
samer@0
|
15 #define _USE_MATH_DEFINES 1
|
samer@0
|
16
|
samer@0
|
17 #include <math.h>
|
samer@0
|
18 #include <float.h>
|
samer@0
|
19 #include "rndutils.h"
|
samer@0
|
20
|
samer@0
|
21 #if HAVE_GSL
|
samer@0
|
22 #include <gsl/gsl_sf_zeta.h>
|
samer@0
|
23 #else
|
samer@0
|
24 double gsl_sf_zeta(const double s) { return NAN; }
|
samer@0
|
25 double gsl_sf_hzeta(const double s, const double k) { return NAN; }
|
samer@0
|
26 #endif
|
samer@0
|
27
|
samer@0
|
28 #ifndef M_PI
|
samer@0
|
29 #define M_PI 3.14159265358979323846
|
samer@0
|
30 #endif
|
samer@0
|
31
|
samer@0
|
32
|
samer@0
|
33 // -----------------------------------------------------
|
samer@0
|
34
|
samer@0
|
35 // Returns a sample from Normal(0,1)
|
samer@0
|
36 double Normal(RndState *S)
|
samer@0
|
37 {
|
samer@0
|
38 double x=S->prev_normal;
|
samer@0
|
39
|
samer@0
|
40 if(!isnan(x)) {
|
samer@0
|
41 S->prev_normal=NAN;
|
samer@0
|
42 } else {
|
samer@0
|
43 double y,radius;
|
samer@0
|
44
|
samer@0
|
45 /* Generate a random point inside the unit circle */
|
samer@0
|
46 do {
|
samer@0
|
47 x = 2*Uniform(S)-1;
|
samer@0
|
48 y = 2*Uniform(S)-1;
|
samer@0
|
49 radius = (x*x)+(y*y);
|
samer@0
|
50 } while((radius >= 1.0) || (radius == 0.0));
|
samer@0
|
51 /* Box-Muller formula */
|
samer@0
|
52 radius = sqrt(-2*log(radius)/radius);
|
samer@0
|
53 x *= radius;
|
samer@0
|
54 y *= radius;
|
samer@0
|
55 S->prev_normal=y;
|
samer@0
|
56 }
|
samer@0
|
57 return x;
|
samer@0
|
58 }
|
samer@0
|
59
|
samer@0
|
60 /* Returns a sample from Gamma(a, 1).
|
samer@0
|
61 * For Gamma(a,b), scale the result by b.
|
samer@0
|
62 */
|
samer@0
|
63 double Gamma(RndState *S, double a)
|
samer@0
|
64 {
|
samer@0
|
65 /* Algorithm:
|
samer@0
|
66 * G. Marsaglia and W.W. Tsang, A simple method for generating gamma
|
samer@0
|
67 * variables, ACM Transactions on Mathematical Software, Vol. 26, No. 3,
|
samer@0
|
68 * Pages 363-372, September, 2000.
|
samer@0
|
69 * http://portal.acm.org/citation.cfm?id=358414
|
samer@0
|
70 */
|
samer@0
|
71 double boost, d, c, v;
|
samer@0
|
72 if(a < 1) {
|
samer@0
|
73 /* boost using Marsaglia's (1961) method: gam(a) = gam(a+1)*U^(1/a) */
|
samer@0
|
74 boost = exp(log(Uniform(S))/a);
|
samer@0
|
75 a++;
|
samer@0
|
76 }
|
samer@0
|
77 else boost = 1;
|
samer@0
|
78 d = a-1.0/3; c = 1.0/sqrt(9*d);
|
samer@0
|
79 while(1) {
|
samer@0
|
80 double x,u;
|
samer@0
|
81 do {
|
samer@0
|
82 x = Normal(S);
|
samer@0
|
83 v = 1+c*x;
|
samer@0
|
84 } while(v <= 0);
|
samer@0
|
85 v = v*v*v;
|
samer@0
|
86 x = x*x;
|
samer@0
|
87 u = Uniform(S);
|
samer@0
|
88 if((u < 1-.0331*x*x) ||
|
samer@0
|
89 (log(u) < 0.5*x + d*(1-v+log(v)))) break;
|
samer@0
|
90 }
|
samer@0
|
91 return( boost*d*v );
|
samer@0
|
92 }
|
samer@0
|
93
|
samer@0
|
94 /* Returns a sample from Dirichlet(n,a) where n is dimensionality */
|
samer@0
|
95 int Dirichlet(RndState *S, long n, double *a, double *x)
|
samer@0
|
96 {
|
samer@0
|
97 long i;
|
samer@0
|
98 double tot=0, z;
|
samer@0
|
99 for (i=0; i<n; i++) { z=Gamma(S,a[i]); tot+=z; x[i]=z; }
|
samer@0
|
100 for (i=0; i<n; i++) { x[i]/=tot; }
|
samer@0
|
101 return 1;
|
samer@0
|
102 }
|
samer@0
|
103
|
samer@0
|
104 /* Returns a sample from Beta(a,b) */
|
samer@0
|
105 double Beta(RndState *S, double a, double b)
|
samer@0
|
106 {
|
samer@0
|
107 double g = Gamma(S,a);
|
samer@0
|
108 return g/(g + Gamma(S,b));
|
samer@0
|
109 }
|
samer@0
|
110
|
samer@0
|
111 double Hyperbolic(RndState *S, double a)
|
samer@0
|
112 {
|
samer@0
|
113 return floor(pow(1-Uniform(S),-1/a));
|
samer@0
|
114 }
|
samer@0
|
115
|
samer@0
|
116 /* Sample from zeta distribution
|
samer@0
|
117 *
|
samer@0
|
118 * This is an inverse CDF method. It works by using an
|
samer@0
|
119 * approximation of the Hurwitz Zeta function and then
|
samer@0
|
120 * walking left or right until we get to the correct point.
|
samer@0
|
121 * The approximation is good for large values so usually we
|
samer@0
|
122 * only have to walk left or right a few steps for small
|
samer@0
|
123 * values.
|
samer@0
|
124 */
|
samer@0
|
125 double Zeta(RndState *S, double s)
|
samer@0
|
126 {
|
samer@0
|
127 double v=(1-Uniform(S))*gsl_sf_zeta(s);
|
samer@0
|
128 double k0, k=round(pow(v*(s-1),1/(1-s))); // approx inverse of hzeta
|
samer@0
|
129 double zk0, zk=gsl_sf_hzeta(s,k);
|
samer@0
|
130
|
samer@0
|
131 if (zk>=v) {
|
samer@0
|
132 do {
|
samer@0
|
133 k0=k; zk0=zk;
|
samer@0
|
134 k=k0+1; zk=zk0-pow(k0,-s);
|
samer@0
|
135 } while (zk>=v && zk!=zk0);
|
samer@0
|
136 return k0;
|
samer@0
|
137 } else {
|
samer@0
|
138 do {
|
samer@0
|
139 k0=k; zk0=zk;
|
samer@0
|
140 k=k0-1; zk=zk0+pow(k,-s);
|
samer@0
|
141 } while (zk<v && zk!=zk0);
|
samer@0
|
142 return k;
|
samer@0
|
143 }
|
samer@0
|
144 }
|
samer@0
|
145
|
samer@0
|
146 /* Very fast binomial sampler.
|
samer@0
|
147 * Returns the number of successes out of n trials, with success probability p.
|
samer@0
|
148 */
|
samer@0
|
149 int Binomial(RndState *S, double p, int n)
|
samer@0
|
150 {
|
samer@0
|
151 int r = 0;
|
samer@0
|
152 if(isnan(p)) return 0;
|
samer@0
|
153 if(p < DBL_EPSILON) return 0;
|
samer@0
|
154 if(p >= 1-DBL_EPSILON) return n;
|
samer@0
|
155 if((p > 0.5) && (n < 15)) {
|
samer@0
|
156 /* Coin flip method. This takes O(n) time. */
|
samer@0
|
157 int i;
|
samer@0
|
158 for(i=0;i<n;i++) {
|
samer@0
|
159 if(Uniform(S) < p) r++;
|
samer@0
|
160 }
|
samer@0
|
161 return r;
|
samer@0
|
162 }
|
samer@0
|
163 if(n*p < 10) {
|
samer@0
|
164 /* Waiting time method. This takes O(np) time. */
|
samer@0
|
165 double q = -log(1-p), e = -log(Uniform(S)), s;
|
samer@0
|
166 r = n;
|
samer@0
|
167 for(s = e/r; s <= q; s += e/r) {
|
samer@0
|
168 r--;
|
samer@0
|
169 if(r == 0) break;
|
samer@0
|
170 e = -log(Uniform(S));
|
samer@0
|
171 }
|
samer@0
|
172 r = n-r;
|
samer@0
|
173 return r;
|
samer@0
|
174 }
|
samer@0
|
175 if (1) {
|
samer@0
|
176 /* Recursive method. This makes O(log(log(n))) recursive calls. */
|
samer@0
|
177 int i = (int)floor(p*(n+1));
|
samer@0
|
178 double b = Beta(S,i, n+1-i);
|
samer@0
|
179 if(b <= p) r = i + Binomial(S,(p-b)/(1-b), n-i);
|
samer@0
|
180 else r = i - 1 - Binomial(S,(b-p)/b, i-1);
|
samer@0
|
181 return r;
|
samer@0
|
182 }
|
samer@0
|
183 }
|
samer@0
|
184
|
samer@0
|
185 double Exponential(RndState *S) { return -log(1-Uniform(S)); }
|
samer@0
|
186
|
samer@0
|
187 long Poisson(RndState *S, double lambda)
|
samer@0
|
188 {
|
samer@0
|
189 long r;
|
samer@0
|
190 if (lambda>=15) {
|
samer@0
|
191 double m=floor(lambda*7/8);
|
samer@0
|
192 double x=Gamma(S,m);
|
samer@0
|
193
|
samer@0
|
194 if (x>lambda) r=Binomial(S,lambda/x,m-1);
|
samer@0
|
195 else r=m+Poisson(S,lambda-x);
|
samer@0
|
196 } else {
|
samer@0
|
197 double p;
|
samer@0
|
198 for (p=0, r=-1; p<lambda; p+=Exponential(S)) r++;
|
samer@0
|
199 }
|
samer@0
|
200 return r;
|
samer@0
|
201 }
|
samer@0
|
202
|
samer@0
|
203 /* Returns a sample from stable distribution */
|
samer@0
|
204 double Stable(RndState *S, int param, double alpha, double beta)
|
samer@0
|
205 {
|
samer@0
|
206 double X, theta=M_PI*(Uniform(S)-0.5);
|
samer@0
|
207
|
samer@0
|
208 // These methods come from John Nolan's book about
|
samer@0
|
209 // stable distributions. They return standardised stable random
|
samer@0
|
210 // numbers in the S(alpha,beta;0) parameterisation
|
samer@0
|
211
|
samer@0
|
212 if (beta==0) {
|
samer@0
|
213 if (alpha==1) {
|
samer@0
|
214 X=tan(theta);
|
samer@0
|
215 } else {
|
samer@0
|
216 double W=Exponential(S);
|
samer@0
|
217 X=sin(alpha*theta)/cos(theta)*pow(cos((alpha-1)*theta)/(W*cos(theta)),1/alpha-1);
|
samer@0
|
218 }
|
samer@0
|
219 } else {
|
samer@0
|
220 double W=Exponential(S);
|
samer@0
|
221 double zeta=beta*tan(alpha*M_PI/2);
|
samer@0
|
222 if (alpha==1) {
|
samer@0
|
223 double b=2*beta/M_PI;
|
samer@0
|
224 double bt=1+b*theta;
|
samer@0
|
225 X=bt*tan(theta) - b*log(W*cos(theta)/bt);
|
samer@0
|
226 } else {
|
samer@0
|
227 double ath = alpha*theta;
|
samer@0
|
228 double a1th = theta-ath;
|
samer@0
|
229 X = ((sin(ath) + zeta*cos(ath))/cos(theta))
|
samer@0
|
230 * pow((cos(a1th) + zeta*sin(a1th))/(W*cos(theta)),1/alpha-1);
|
samer@0
|
231 if (param==0) X=X-zeta;
|
samer@0
|
232 }
|
samer@0
|
233 }
|
samer@0
|
234 return X;
|
samer@0
|
235 }
|
samer@0
|
236
|
samer@0
|
237 /* Returns a sample from discrete distribution */
|
samer@0
|
238 long Discrete(RndState *S, long n, double *p, double tot)
|
samer@0
|
239 {
|
samer@0
|
240 double u;
|
samer@0
|
241 long i;
|
samer@0
|
242
|
samer@0
|
243 for (i=0, u=tot*Uniform(S)-p[0]; u>=0; u-=p[++i]);
|
samer@0
|
244 return i;
|
samer@0
|
245 }
|
samer@0
|
246
|
samer@0
|
247 double Uniform(RndState *S0)
|
samer@0
|
248 {
|
samer@0
|
249 return RngStream_Double(&S0->g,&S0->g);
|
samer@0
|
250 }
|
samer@0
|
251
|
samer@0
|
252 double Raw(RndState *S0)
|
samer@0
|
253 {
|
samer@0
|
254 return RngStream_Raw(&S0->g,&S0->g);
|
samer@0
|
255 }
|
samer@0
|
256
|
samer@0
|
257 void InitRndState(RndState *S)
|
samer@0
|
258 {
|
samer@0
|
259 RngStream_InitState(&S->g);
|
samer@0
|
260 S->prev_normal=NAN;
|
samer@0
|
261 }
|
samer@0
|
262
|
samer@0
|
263
|
samer@0
|
264 int spawn_gen(RndState *S0, RndState *S1)
|
samer@0
|
265 {
|
samer@0
|
266 unsigned seeds[6];
|
samer@0
|
267 int i;
|
samer@0
|
268
|
samer@0
|
269 for (i=0; i<6; i++) seeds[i]=(unsigned)RngStream_Raw(&S0->g,&S0->g);
|
samer@0
|
270 RngStream_SetState(&S1->g,seeds);
|
samer@0
|
271 for (i=0; i<3; i++) RngStream_Raw(&S1->g,&S1->g);
|
samer@0
|
272 S1->prev_normal=NAN;
|
samer@0
|
273 return 1;
|
samer@0
|
274 }
|
samer@0
|
275
|
samer@0
|
276 int randomise_from(FILE *p, RndState *S)
|
samer@0
|
277 {
|
samer@0
|
278 unsigned seeds[6];
|
samer@0
|
279 size_t r=fread((void *)seeds,sizeof(unsigned),6,p);
|
samer@0
|
280
|
samer@0
|
281 if (r==6) {
|
samer@0
|
282 int i;
|
samer@0
|
283 if (RngStream_SetState(&S->g,seeds)<0) return 0;
|
samer@0
|
284 for (i=0; i<3; i++) RngStream_Raw(&S->g,&S->g);
|
samer@0
|
285 S->prev_normal=NAN;
|
samer@0
|
286 return 1;
|
samer@0
|
287 } else return 0;
|
samer@0
|
288 }
|
samer@0
|
289
|