Geant4  10.01.p02
RandPoissonQ.cc
Go to the documentation of this file.
1 // $Id:$
2 // -*- C++ -*-
3 //
4 // -----------------------------------------------------------------------
5 // HEP Random
6 // --- RandPoissonQ ---
7 // class implementation file
8 // -----------------------------------------------------------------------
9 
10 // =======================================================================
11 // M. Fischler - Implemented new, much faster table-driven algorithm
12 // applicable for mu < 100
13 // - Implemented "quick()" methods, shich are the same as the
14 // new methods for mu < 100 and are a skew-corrected gaussian
15 // approximation for large mu.
16 // M. Fischler - Removed mean=100 from the table-driven set, since it
17 // uses a value just off the end of the table. (April 2004)
18 // M Fischler - put and get to/from streams 12/15/04
19 // M Fischler - Utilize RandGaussQ rather than RandGauss, as clearly
20 // intended by the inclusion of RandGaussQ.h. Using RandGauss
21 // introduces a subtle trap in that the state of RandPoissonQ
22 // can never be properly captured without also saveing the
23 // state of RandGauss! RandGaussQ is, on the other hand,
24 // stateless except for the engine used.
25 // M Fisculer - Modified use of wrong engine when shoot (anEngine, mean)
26 // is called. This flaw was preventing any hope of proper
27 // saving and restoring in the instance cases.
28 // M Fischler - fireArray using defaultMean 2/10/05
29 // M Fischler - put/get to/from streams uses pairs of ulongs when
30 // + storing doubles avoid problems with precision
31 // 4/14/05
32 // M Fisculer - Modified use of shoot (mean) instead of
33 // shoot(getLocalEngine(), mean) when fire(mean) is called.
34 // This flaw was causing bad "cross-talk" between modules
35 // in CMS, where one used its own engine, and the other
36 // used the static generator. 10/18/07
37 //
38 // =======================================================================
39 
40 #include "CLHEP/Random/RandPoissonQ.h"
41 #include "CLHEP/Random/RandGaussQ.h"
42 #include "CLHEP/Random/DoubConv.h"
43 #include "CLHEP/Random/Stat.h"
44 #include "CLHEP/Utility/thread_local.h"
45 #include <cmath> // for std::pow()
46 
47 namespace CLHEP {
48 
49 std::string RandPoissonQ::name() const {return "RandPoissonQ";}
50 HepRandomEngine & RandPoissonQ::engine() {return RandPoisson::engine();}
51 
52 // Initialization of static data: Note that this is all const static data,
53 // so that saveEngineStatus properly saves all needed information.
54 
55  // The following MUST MATCH the corresponding values used (in
56  // poissonTables.cc) when poissonTables.cdat was created.
57 
58 const double RandPoissonQ::FIRST_MU = 10;// lowest mu value in table
59 const double RandPoissonQ::LAST_MU = 95;// highest mu value
60 const double RandPoissonQ::S = 5; // Spacing between mu values
61 const int RandPoissonQ::BELOW = 30; // Starting point for N is at mu - BELOW
62 const int RandPoissonQ::ENTRIES = 51; // Number of entries in each mu row
63 
64 const double RandPoissonQ::MAXIMUM_POISSON_DEVIATE = 2.0E9;
65  // Careful -- this is NOT the maximum number that can be held in
66  // a long. It actually should be some large number of sigma below
67  // that.
68 
69  // Here comes the big (9K bytes) table, kept in a file of
70  // ENTRIES * (FIRST_MU - LAST_MU + 1)/S doubles
71 
72 static const double poissonTables [ 51 * ( (95-10)/5 + 1 ) ] = {
73 #include "CLHEP/Random/poissonTables.cdat"
74 };
75 
76 //
77 // Constructors and destructors:
78 //
79 
80 RandPoissonQ::~RandPoissonQ() {
81 }
82 
83 void RandPoissonQ::setupForDefaultMu() {
84 
85  // The following are useful for quick approximation, for large mu
86 
87  double sig2 = defaultMean * (.9998654 - .08346/defaultMean);
88  sigma = std::sqrt(sig2);
89  // sigma for the Guassian which approximates the Poisson -- naively
90  // sqrt (defaultMean).
91  //
92  // The multiplier corrects for fact that discretization of the form
93  // [gaussian+.5] increases the second moment by a small amount.
94 
95  double t = 1./(sig2);
96 
97  a2 = t/6 + t*t/324;
98  a1 = std::sqrt (1-2*a2*a2*sig2);
99  a0 = defaultMean + .5 - sig2 * a2;
100 
101  // The formula will be a0 + a1*x + a2*x*x where x has 2nd moment of sigma.
102  // The coeffeicients are chosen to match the first THREE moments of the
103  // true Poisson distribution.
104  //
105  // Actually, if the correction for discretization were not needed, then
106  // a2 could be taken one order higher by adding t*t*t/5832. However,
107  // the discretization correction is not perfect, leading to inaccuracy
108  // on the order to 1/mu**2, so adding a third term is overkill.
109 
110 } // setupForDefaultMu()
111 
112 
113 //
114 // fire, quick, operator(), and shoot methods:
115 //
116 
117 long RandPoissonQ::shoot(double xm) {
118  return shoot(getTheEngine(), xm);
119 }
120 
121 double RandPoissonQ::operator()() {
122  return (double) fire();
123 }
124 
125 double RandPoissonQ::operator()( double mean ) {
126  return (double) fire(mean);
127 }
128 
129 long RandPoissonQ::fire(double mean) {
130  return shoot(getLocalEngine(), mean);
131 }
132 
133 long RandPoissonQ::fire() {
134  if ( defaultMean < LAST_MU + S ) {
135  return poissonDeviateSmall ( getLocalEngine(), defaultMean );
136  } else {
137  return poissonDeviateQuick ( getLocalEngine(), a0, a1, a2, sigma );
138  }
139 } // fire()
140 
141 long RandPoissonQ::shoot(HepRandomEngine* anEngine, double mean) {
142 
143  // The following variables, static to this method, apply to the
144  // last time a large mean was supplied; they obviate certain calculations
145  // if consecutive calls use the same mean.
146 
147  static CLHEP_THREAD_LOCAL double lastLargeMean = -1.; // Mean from previous shoot
148  // requiring poissonDeviateQuick()
149  static CLHEP_THREAD_LOCAL double lastA0;
150  static CLHEP_THREAD_LOCAL double lastA1;
151  static CLHEP_THREAD_LOCAL double lastA2;
152  static CLHEP_THREAD_LOCAL double lastSigma;
153 
154  if ( mean < LAST_MU + S ) {
155  return poissonDeviateSmall ( anEngine, mean );
156  } else {
157  if ( mean != lastLargeMean ) {
158  // Compute the coefficients defining the quadratic transformation from a
159  // Gaussian to a Poisson for this mean. Also save these for next time.
160  double sig2 = mean * (.9998654 - .08346/mean);
161  lastSigma = std::sqrt(sig2);
162  double t = 1./sig2;
163  lastA2 = t*(1./6.) + t*t*(1./324.);
164  lastA1 = std::sqrt (1-2*lastA2*lastA2*sig2);
165  lastA0 = mean + .5 - sig2 * lastA2;
166  }
167  return poissonDeviateQuick ( anEngine, lastA0, lastA1, lastA2, lastSigma );
168  }
169 
170 } // shoot (anEngine, mean)
171 
172 void RandPoissonQ::shootArray(const int size, long* vect, double m) {
173  for( long* v = vect; v != vect + size; ++v )
174  *v = shoot(m);
175  // Note: We could test for m > 100, and if it is, precompute a0, a1, a2,
176  // and sigma and call the appropriate form of poissonDeviateQuick.
177  // But since those are cached anyway, not much time would be saved.
178 }
179 
180 void RandPoissonQ::fireArray(const int size, long* vect, double m) {
181  for( long* v = vect; v != vect + size; ++v )
182  *v = fire( m );
183 }
184 
185 void RandPoissonQ::fireArray(const int size, long* vect) {
186  for( long* v = vect; v != vect + size; ++v )
187  *v = fire( defaultMean );
188 }
189 
190 
191 // Quick Poisson deviate algorithm used by quick for large mu:
192 
193 long RandPoissonQ::poissonDeviateQuick ( HepRandomEngine *e, double mu ) {
194 
195  // Compute the coefficients defining the quadratic transformation from a
196  // Gaussian to a Poisson:
197 
198  double sig2 = mu * (.9998654 - .08346/mu);
199  double sig = std::sqrt(sig2);
200  // The multiplier corrects for fact that discretization of the form
201  // [gaussian+.5] increases the second moment by a small amount.
202 
203  double t = 1./sig2;
204 
205  double sa2 = t*(1./6.) + t*t*(1./324.);
206  double sa1 = std::sqrt (1-2*sa2*sa2*sig2);
207  double sa0 = mu + .5 - sig2 * sa2;
208 
209  // The formula will be sa0 + sa1*x + sa2*x*x where x has sigma of sq.
210  // The coeffeicients are chosen to match the first THREE moments of the
211  // true Poisson distribution.
212 
213  return poissonDeviateQuick ( e, sa0, sa1, sa2, sig );
214 }
215 
216 
217 long RandPoissonQ::poissonDeviateQuick ( HepRandomEngine *e,
218  double A0, double A1, double A2, double sig) {
219 //
220 // Quick Poisson deviate algorithm used by quick for large mu:
221 //
222 // The principle: For very large mu, a poisson distribution can be approximated
223 // by a gaussian: return the integer part of mu + .5 + g where g is a unit
224 // normal. However, this yelds a miserable approximation at values as
225 // "large" as 100. The primary problem is that the poisson distribution is
226 // supposed to have a skew of 1/mu**2, and the zero skew of the Guassian
227 // leads to errors of order as big as 1/mu**2.
228 //
229 // We substitute for the gaussian a quadratic function of that gaussian random.
230 // The expression looks very nearly like mu + .5 - 1/6 + g + g**2/(6*mu).
231 // The small positive quadratic term causes the resulting variate to have
232 // a positive skew; the -1/6 constant term is there to correct for this bias
233 // in the mean. By adjusting these two and the linear term, we can match the
234 // first three moments to high accuracy in 1/mu.
235 //
236 // The sigma used is not precisely sqrt(mu) since a rounded-off Gaussian
237 // has a second moment which is slightly larger than that of the Gaussian.
238 // To compensate, sig is multiplied by a factor which is slightly less than 1.
239 
240  // double g = RandGauss::shootQuick( e ); // TEMPORARY MOD:
241  double g = RandGaussQ::shoot( e ); // Unit normal
242  g *= sig;
243  double p = A2*g*g + A1*g + A0;
244  if ( p < 0 ) return 0; // Shouldn't ever possibly happen since
245  // mean should not be less than 100, but
246  // we check due to paranoia.
247  if ( p > MAXIMUM_POISSON_DEVIATE ) p = MAXIMUM_POISSON_DEVIATE;
248 
249  return long(p);
250 
251 } // poissonDeviateQuick ()
252 
253 
254 
255 long RandPoissonQ::poissonDeviateSmall (HepRandomEngine * e, double mean) {
256  long N1;
257  long N2;
258  // The following are for later use to form a secondary random s:
259  double rRange; // This will hold the interval between cdf for the
260  // computed N1 and cdf for N1+1.
261  double rRemainder = 0; // This will hold the length into that interval.
262 
263  // Coming in, mean should not be more than LAST_MU + S. However, we will
264  // be paranoid and test for this:
265 
266  if ( mean > LAST_MU + S ) {
267  return RandPoisson::shoot(e, mean);
268  }
269 
270  if (mean <= 0) {
271  return 0; // Perhaps we ought to balk harder here!
272  }
273 
274  // >>> 1 <<<
275  // Generate the first random, which we always will need.
276 
277  double r = e->flat();
278 
279  // >>> 2 <<<
280  // For small mean, below the start of the tables,
281  // do the series for cdf directly.
282 
283  // In this case, since we know the series will terminate relatively quickly,
284  // almost alwaye use a precomputed 1/N array without fear of overrunning it.
285 
286  static const double oneOverN[50] =
287  { 0, 1., 1/2., 1/3., 1/4., 1/5., 1/6., 1/7., 1/8., 1/9.,
288  1/10., 1/11., 1/12., 1/13., 1/14., 1/15., 1/16., 1/17., 1/18., 1/19.,
289  1/20., 1/21., 1/22., 1/23., 1/24., 1/25., 1/26., 1/27., 1/28., 1/29.,
290  1/30., 1/31., 1/32., 1/33., 1/34., 1/35., 1/36., 1/37., 1/38., 1/39.,
291  1/40., 1/41., 1/42., 1/43., 1/44., 1/45., 1/46., 1/47., 1/48., 1/49. };
292 
293 
294  if ( mean < FIRST_MU ) {
295 
296  long N = 0;
297  double term = std::exp(-mean);
298  double cdf = term;
299 
300  if ( r < (1 - 1.0E-9) ) {
301  //
302  // **** This is a normal path: ****
303  //
304  // Except when r is very close to 1, it is certain that we will exceed r
305  // before the 30-th term in the series, so a simple while loop is OK.
306  const double* oneOverNptr = oneOverN;
307  while( cdf <= r ) {
308  N++ ;
309  oneOverNptr++;
310  term *= ( mean * (*oneOverNptr) );
311  cdf += term;
312  }
313  return N;
314  //
315  // **** ****
316  //
317  } else { // r is almost 1...
318  // For r very near to 1 we would have to check that we don't fall
319  // off the end of the table of 1/N. Since this is very rare, we just
320  // ignore the table and do the identical while loop, using explicit
321  // division.
322  double cdf0;
323  while ( cdf <= r ) {
324  N++ ;
325  term *= ( mean / N );
326  cdf0 = cdf;
327  cdf += term;
328  if (cdf == cdf0) break; // Can't happen, but just in case...
329  }
330  return N;
331  } // end of if ( r compared to (1 - 1.0E-9) )
332 
333  } // End of the code for mean < FIRST_MU
334 
335  // >>> 3 <<<
336  // Find the row of the tables corresponding to the highest tabulated mu
337  // which is no greater than our actual mean.
338 
339  int rowNumber = int((mean - FIRST_MU)/S);
340  const double * cdfs = &poissonTables [rowNumber*ENTRIES];
341  double mu = FIRST_MU + rowNumber*S;
342  double deltaMu = mean - mu;
343  int Nmin = int(mu - BELOW);
344  if (Nmin < 1) Nmin = 1;
345  int Nmax = Nmin + (ENTRIES - 1);
346 
347 
348  // >>> 4 <<<
349  // If r is less that the smallest entry in the row, then
350  // generate the deviate directly from the series.
351 
352  if ( r < cdfs[0] ) {
353 
354  // In this case, we are tempted to use the actual mean, and not
355  // generate a second deviate to account for the leftover part mean - mu.
356  // That would be an error, generating a distribution with enough excess
357  // at Nmin + (mean-mu)/2 to be detectable in 4,000,000 trials.
358 
359  // Since this case is very rare (never more than .2% of the r values)
360  // and can happen where N will be large (up to 65 for the mu=95 row)
361  // we use explicit division so as to avoid having to worry about running
362  // out of oneOverN table.
363 
364  long N = 0;
365  double term = std::exp(-mu);
366  double cdf = term;
367  double cdf0;
368 
369  while(cdf <= r) {
370  N++ ;
371  term *= ( mu / N );
372  cdf0 = cdf;
373  cdf += term;
374  if (cdf == cdf0) break; // Can't happen, but just in case...
375  }
376  N1 = N;
377  // std::cout << r << " " << N << " ";
378  // DBG_small = true;
379  rRange = 0; // In this case there is always a second r needed
380 
381  } // end of small-r case
382 
383 
384  // >>> 5 <<<
385  // Assuming r lies within the scope of the row for this mu, find the
386  // largest entry not greater than r. N1 is the N corresponding to the
387  // index a.
388 
389  else if ( r < cdfs[ENTRIES-1] ) { // r is also >= cdfs[0]
390 
391  //
392  // **** This is the normal code path ****
393  //
394 
395  int a = 0; // Highest value of index such that cdfs[a]
396  // is known NOT to be greater than r.
397  int b = ENTRIES - 1; // Lowest value of index such that cdfs[b] is
398  // known to exeed r.
399 
400  while (b != (a+1) ) {
401  int c = (a+b+1)>>1;
402  if (r > cdfs[c]) {
403  a = c;
404  } else {
405  b = c;
406  }
407  }
408 
409  N1 = Nmin + a;
410  rRange = cdfs[a+1] - cdfs[a];
411  rRemainder = r - cdfs[a];
412 
413  //
414  // **** ****
415  //
416 
417  } // end of medium-r (normal) case
418 
419 
420  // >>> 6 <<<
421  // If r exceeds the greatest entry in the table for this mu, then start
422  // from that cdf, and use the series to compute from there until r is
423  // exceeded.
424 
425  else { // if ( r >= cdfs[ENTRIES-1] ) {
426 
427  // Here, division must be done explicitly, and we must also protect against
428  // roundoff preventing termination.
429 
430  //
431  //+++ cdfs[ENTRIES-1] is exp(-mu) sum (mu**m/m! , m=0 to Nmax)
432  //+++ (where Nmax = mu - BELOW + ENTRIES - 1)
433  //+++ cdfs[ENTRIES-1]-cdfs[ENTRIES-2] is exp(-mu) mu**(Nmax)/(Nmax)!
434  //+++ If the sum up to k-1 <= r < sum up to k, then N = k-1
435  //+++ Consider k = Nmax in the above statement:
436  //+++ If cdfs[ENTRIES-2] <= r < cdfs[ENTRIES-1], N would be Nmax-1
437  //+++ But here r >= cdfs[ENTRIES-1] so N >= Nmax
438  //
439 
440  // Erroneous:
441  //+++ cdfs[ENTRIES-1] is exp(-mu) sum (mu**m/m! , m=0 to Nmax-1)
442  //+++ cdfs[ENTRIES-1]-cdfs[ENTRIES-2] is exp(-mu) mu**(Nmax-1)/(Nmax-1)!
443  //+++ If a sum up to k-1 <= r < sum up to k, then N = k-1
444  //+++ So if cdfs[ENTRIES-1] were > r, N would be Nmax-1 (or less)
445  //+++ But here r >= cdfs[ENTRIES-1] so N >= Nmax
446  //
447 
448  // std::cout << "r = " << r << " mu = " << mu << "\n";
449  long N = Nmax -1;
450  double cdf = cdfs[ENTRIES-1];
451  double term = cdf - cdfs[ENTRIES-2];
452  double cdf0;
453  while(cdf <= r) {
454  N++ ;
455  // std::cout << " N " << N << " term " <<
456  // term << " cdf " << cdf << "\n";
457  term *= ( mu / N );
458  cdf0 = cdf;
459  cdf += term;
460  if (cdf == cdf0) break; // If term gets so small cdf stops increasing,
461  // terminate using that value of N since we
462  // would never reach r.
463  }
464  N1 = N;
465  rRange = 0; // We can't validly omit the second true random
466 
467  // N = Nmax -1;
468  // cdf = cdfs[ENTRIES-1];
469  // term = cdf - cdfs[ENTRIES-2];
470  // for (int isxz=0; isxz < 100; isxz++) {
471  // N++ ;
472  // term *= ( mu / N );
473  // cdf0 = cdf;
474  // cdf += term;
475  // }
476  // std::cout.precision(20);
477  // std::cout << "Final sum is " << cdf << "\n";
478 
479  } // end of large-r case
480 
481 
482 
483  // >>> 7 <<<
484  // Form a second random, s, based on the position of r within the range
485  // of this table entry to the next entry.
486 
487  // However, if this range is very small, then we lose too many bits of
488  // randomness. In that situation, we generate a second random for s.
489 
490  double s;
491 
492  static const double MINRANGE = .01; // Sacrifice up to two digits of
493  // randomness when using r to produce
494  // a second random s. Leads to up to
495  // .09 extra randoms each time.
496 
497  if ( rRange > MINRANGE ) {
498  //
499  // **** This path taken 90% of the time ****
500  //
501  s = rRemainder / rRange;
502  } else {
503  s = e->flat(); // extra true random needed about one time in 10.
504  }
505 
506  // >>> 8 <<<
507  // Use the direct summation method to form a second poisson deviate N2
508  // from deltaMu and s.
509 
510  N2 = 0;
511  double term = std::exp(-deltaMu);
512  double cdf = term;
513 
514  if ( s < (1 - 1.0E-10) ) {
515  //
516  // This is the normal path:
517  //
518  const double* oneOverNptr = oneOverN;
519  while( cdf <= s ) {
520  N2++ ;
521  oneOverNptr++;
522  term *= ( deltaMu * (*oneOverNptr) );
523  cdf += term;
524  }
525  } else { // s is almost 1...
526  while( cdf <= s ) {
527  N2++ ;
528  term *= ( deltaMu / N2 );
529  cdf += term;
530  }
531  } // end of if ( s compared to (1 - 1.0E-10) )
532 
533  // >>> 9 <<<
534  // The result is the sum of those two deviates
535 
536  // if (DBG_small) {
537  // std::cout << N2 << " " << N1+N2 << "\n";
538  // DBG_small = false;
539  // }
540 
541  return N1 + N2;
542 
543 } // poissonDeviate()
544 
545 std::ostream & RandPoissonQ::put ( std::ostream & os ) const {
546  int pr=os.precision(20);
547  std::vector<unsigned long> t(2);
548  os << " " << name() << "\n";
549  os << "Uvec" << "\n";
550  t = DoubConv::dto2longs(a0);
551  os << a0 << " " << t[0] << " " << t[1] << "\n";
552  t = DoubConv::dto2longs(a1);
553  os << a1 << " " << t[0] << " " << t[1] << "\n";
554  t = DoubConv::dto2longs(a2);
555  os << a2 << " " << t[0] << " " << t[1] << "\n";
556  t = DoubConv::dto2longs(sigma);
557  os << sigma << " " << t[0] << " " << t[1] << "\n";
558  RandPoisson::put(os);
559  os.precision(pr);
560  return os;
561 #ifdef REMOVED
562  int pr=os.precision(20);
563  os << " " << name() << "\n";
564  os << a0 << " " << a1 << " " << a2 << "\n";
565  os << sigma << "\n";
566  RandPoisson::put(os);
567  os.precision(pr);
568  return os;
569 #endif
570 }
571 
572 std::istream & RandPoissonQ::get ( std::istream & is ) {
573  std::string inName;
574  is >> inName;
575  if (inName != name()) {
576  is.clear(std::ios::badbit | is.rdstate());
577  std::cerr << "Mismatch when expecting to read state of a "
578  << name() << " distribution\n"
579  << "Name found was " << inName
580  << "\nistream is left in the badbit state\n";
581  return is;
582  }
583  if (possibleKeywordInput(is, "Uvec", a0)) {
584  std::vector<unsigned long> t(2);
585  is >> a0 >> t[0] >> t[1]; a0 = DoubConv::longs2double(t);
586  is >> a1 >> t[0] >> t[1]; a1 = DoubConv::longs2double(t);
587  is >> a2 >> t[0] >> t[1]; a2 = DoubConv::longs2double(t);
588  is >> sigma >> t[0] >> t[1]; sigma = DoubConv::longs2double(t);
589  RandPoisson::get(is);
590  return is;
591  }
592  // is >> a0 encompassed by possibleKeywordInput
593  is >> a1 >> a2 >> sigma;
594  RandPoisson::get(is);
595  return is;
596 }
597 
598 } // namespace CLHEP
599 
const G4double a0
ThreeVector shoot(const G4int Ap, const G4int Af)
G4String name
Definition: TRTMaterials.hh:40
static const G4double a1
static int engine(pchar, pchar, double &, pchar &, const dic_type &)
Definition: Evaluator.cc:358
G4double a
Definition: TRTMaterials.hh:39
static const double s
Definition: G4SIunits.hh:150
static const double poissonTables[51 *((95-10)/5+1)]
Definition: RandPoissonQ.cc:72
static const double g
Definition: G4SIunits.hh:162
static const double m
Definition: G4SIunits.hh:110
static const G4double a2