annotate rndutils.c @ 3:974d7be8eec4 tip

Update to pack-based dcg utilities
author samer
date Tue, 03 Oct 2017 11:52:23 +0100
parents b31415b4a196
children
rev   line source
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