GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
randpoisson.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2006-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26/* Original version written by Paul Kienzle distributed as free
27 software in the in the public domain. */
28
29#if defined (HAVE_CONFIG_H)
30# include "config.h"
31#endif
32
33#include <cmath>
34#include <cstddef>
35
36#include "f77-fcn.h"
37#include "lo-error.h"
38#include "lo-ieee.h"
39#include "lo-mappers.h"
40#include "randmtzig.h"
41#include "randpoisson.h"
42
44
45static double xlgamma (double x)
46{
47 return std::lgamma (x);
48}
49
50/* ---- pprsc.c from Stadloeber's winrand --- */
51
52/* flogfak(k) = ln(k!) */
53static double
54flogfak (double k)
55{
56#define C0 9.18938533204672742e-01
57#define C1 8.33333333333333333e-02
58#define C3 -2.77777777777777778e-03
59#define C5 7.93650793650793651e-04
60#define C7 -5.95238095238095238e-04
61
62 static double logfak[30L] =
63 {
64 0.00000000000000000, 0.00000000000000000, 0.69314718055994531,
65 1.79175946922805500, 3.17805383034794562, 4.78749174278204599,
66 6.57925121201010100, 8.52516136106541430, 10.60460290274525023,
67 12.80182748008146961, 15.10441257307551530, 17.50230784587388584,
68 19.98721449566188615, 22.55216385312342289, 25.19122118273868150,
69 27.89927138384089157, 30.67186010608067280, 33.50507345013688888,
70 36.39544520803305358, 39.33988418719949404, 42.33561646075348503,
71 45.38013889847690803, 48.47118135183522388, 51.60667556776437357,
72 54.78472939811231919, 58.00360522298051994, 61.26170176100200198,
73 64.55753862700633106, 67.88974313718153498, 71.25703896716800901
74 };
75
76 double r, rr;
77
78 if (k >= 30.0)
79 {
80 r = 1.0 / k;
81 rr = r * r;
82 return ((k + 0.5)*std::log (k) - k + C0
83 + r*(C1 + rr*(C3 + rr*(C5 + rr*C7))));
84 }
85 else
86 return (logfak[static_cast<int> (k)]);
87}
88
89/******************************************************************
90 * *
91 * Poisson Distribution - Patchwork Rejection/Inversion *
92 * *
93 ******************************************************************
94 * *
95 * For parameter my < 10, Tabulated Inversion is applied. *
96 * For my >= 10, Patchwork Rejection is employed: *
97 * The area below the histogram function f(x) is rearranged in *
98 * its body by certain point reflections. Within a large center *
99 * interval variates are sampled efficiently by rejection from *
100 * uniform hats. Rectangular immediate acceptance regions speed *
101 * up the generation. The remaining tails are covered by *
102 * exponential functions. *
103 * *
104 ******************************************************************
105 * *
106 * FUNCTION : - pprsc samples a random number from the Poisson *
107 * distribution with parameter my > 0. *
108 * REFERENCE : - H. Zechner (1994): Efficient sampling from *
109 * continuous and discrete unimodal distributions, *
110 * Doctoral Dissertation, 156 pp., Technical *
111 * University Graz, Austria. *
112 * SUBPROGRAM : - drand(seed) ... (0,1)-Uniform generator with *
113 * unsigned long integer *seed. *
114 * *
115 * Implemented by H. Zechner, January 1994 *
116 * Revised by F. Niederl, July 1994 *
117 * *
118 ******************************************************************/
119
120static double
121f (double k, double l_nu, double c_pm)
122{
123 return exp (k * l_nu - flogfak (k) - c_pm);
124}
125
126static double
127pprsc (double my)
128{
129 static double my_last = -1.0;
130 static double m, k2, k4, k1, k5;
131 static double dl, dr, r1, r2, r4, r5, ll, lr, l_my, c_pm,
132 f1, f2, f4, f5, p1, p2, p3, p4, p5, p6;
133 double Dk, X, Y;
134 double Ds, U, V, W;
135
136 if (my != my_last)
137 {
138 /* set-up */
139 my_last = my;
140 /* approximate deviation of reflection points k2, k4 from my - 1/2 */
141 Ds = std::sqrt (my + 0.25);
142
143 /* mode m, reflection points k2 and k4, and points k1 and k5, */
144 /* which delimit the centre region of h(x) */
145 m = std::floor (my);
146 k2 = ceil (my - 0.5 - Ds);
147 k4 = std::floor (my - 0.5 + Ds);
148 k1 = k2 + k2 - m + 1L;
149 k5 = k4 + k4 - m;
150
151 /* range width of the critical left and right centre region */
152 dl = (k2 - k1);
153 dr = (k5 - k4);
154
155 /* recurrence constants r(k)=p(k)/p(k-1) at k = k1, k2, k4+1, k5+1 */
156 r1 = my / k1;
157 r2 = my / k2;
158 r4 = my / (k4 + 1.0);
159 r5 = my / (k5 + 1.0);
160
161 /* reciprocal values of the scale parameters of exp. tail envelope */
162 ll = std::log (r1); /* expon. tail left */
163 lr = -std::log (r5); /* expon. tail right*/
164
165 /* Poisson constants, necessary for computing function values f(k) */
166 l_my = std::log (my);
167 c_pm = m * l_my - flogfak (m);
168
169 /* function values f(k) = p(k)/p(m) at k = k2, k4, k1, k5 */
170 f2 = f (k2, l_my, c_pm);
171 f4 = f (k4, l_my, c_pm);
172 f1 = f (k1, l_my, c_pm);
173 f5 = f (k5, l_my, c_pm);
174
175 /* area of the two centre and the two exponential tail regions */
176 /* area of the two immediate acceptance regions between k2, k4 */
177 p1 = f2 * (dl + 1.0); /* immed. left */
178 p2 = f2 * dl + p1; /* centre left */
179 p3 = f4 * (dr + 1.0) + p2; /* immed. right */
180 p4 = f4 * dr + p3; /* centre right */
181 p5 = f1 / ll + p4; /* exp. tail left */
182 p6 = f5 / lr + p5; /* exp. tail right*/
183 }
184
185 for (;;)
186 {
187 /* generate uniform number U -- U(0, p6) */
188 /* case distinction corresponding to U */
189 if ((U = rand_uniform<double> () * p6) < p2)
190 {
191 /* centre left */
192
193 /* immediate acceptance region
194 R2 = [k2, m) *[0, f2), X = k2, ... m -1 */
195 if ((V = U - p1) < 0.0) return (k2 + std::floor (U/f2));
196 /* immediate acceptance region
197 R1 = [k1, k2)*[0, f1), X = k1, ... k2-1 */
198 if ((W = V / dl) < f1 ) return (k1 + std::floor (V/f1));
199
200 /* computation of candidate X < k2, and its counterpart Y > k2 */
201 /* either squeeze-acceptance of X or acceptance-rejection of Y */
202 Dk = std::floor (dl * rand_uniform<double> ()) + 1.0;
203 if (W <= f2 - Dk * (f2 - f2/r2))
204 {
205 /* quick accept of */
206 return (k2 - Dk); /* X = k2 - Dk */
207 }
208 if ((V = f2 + f2 - W) < 1.0)
209 {
210 /* quick reject of Y*/
211 Y = k2 + Dk;
212 if (V <= f2 + Dk * (1.0 - f2)/(dl + 1.0))
213 {
214 /* quick accept of */
215 return (Y); /* Y = k2 + Dk */
216 }
217 if (V <= f (Y, l_my, c_pm)) return (Y); /* final accept of Y*/
218 }
219 X = k2 - Dk;
220 }
221 else if (U < p4)
222 {
223 /* centre right */
224 /* immediate acceptance region
225 R3 = [m, k4+1)*[0, f4), X = m, ... k4 */
226 if ((V = U - p3) < 0.0) return (k4 - std::floor ((U - p2)/f4));
227 /* immediate acceptance region
228 R4 = [k4+1, k5+1)*[0, f5) */
229 if ((W = V / dr) < f5 ) return (k5 - std::floor (V/f5));
230
231 /* computation of candidate X > k4, and its counterpart Y < k4 */
232 /* either squeeze-acceptance of X or acceptance-rejection of Y */
233 Dk = std::floor (dr * rand_uniform<double> ()) + 1.0;
234 if (W <= f4 - Dk * (f4 - f4*r4))
235 {
236 /* quick accept of */
237 return (k4 + Dk); /* X = k4 + Dk */
238 }
239 if ((V = f4 + f4 - W) < 1.0)
240 {
241 /* quick reject of Y*/
242 Y = k4 - Dk;
243 if (V <= f4 + Dk * (1.0 - f4)/ dr)
244 {
245 /* quick accept of */
246 return (Y); /* Y = k4 - Dk */
247 }
248 if (V <= f (Y, l_my, c_pm)) return (Y); /* final accept of Y*/
249 }
250 X = k4 + Dk;
251 }
252 else
253 {
255 if (U < p5)
256 {
257 /* expon. tail left */
258 Dk = std::floor (1.0 - std::log (W)/ll);
259 if ((X = k1 - Dk) < 0L) continue; /* 0 <= X <= k1 - 1 */
260 W *= (U - p4) * ll; /* W -- U(0, h(x)) */
261 if (W <= f1 - Dk * (f1 - f1/r1))
262 return (X); /* quick accept of X*/
263 }
264 else
265 {
266 /* expon. tail right*/
267 Dk = std::floor (1.0 - std::log (W)/lr);
268 X = k5 + Dk; /* X >= k5 + 1 */
269 W *= (U - p5) * lr; /* W -- U(0, h(x)) */
270 if (W <= f5 - Dk * (f5 - f5*r5))
271 return (X); /* quick accept of X*/
272 }
273 }
274
275 /* acceptance-rejection test of candidate X from the original area */
276 /* test, whether W <= f(k), with W = U*h(x) and U -- U(0, 1)*/
277 /* log f(X) = (X - m)*log(my) - log X! + log m! */
278 if (std::log (W) <= X * l_my - flogfak (X) - c_pm) return (X);
279 }
280}
281/* ---- pprsc.c end ------ */
282
283/* The remainder of the file is by Paul Kienzle */
284
285/* Table size is predicated on the maximum value of lambda
286 * we want to store in the table, and the maximum value of
287 * returned by the uniform random number generator on [0,1).
288 * With lambda==10 and u_max = 1 - 1/(2^32+1), we
289 * have poisson_pdf(lambda,36) < 1-u_max. If instead our
290 * generator uses more bits of mantissa or returns a value
291 * in the range [0,1], then for lambda==10 we need a table
292 * size of 46 instead. For long doubles, the table size
293 * will need to be longer still. */
294#define TABLESIZE 46
295
296/* Given uniform u, find x such that CDF(L,x)==u. Return x. */
297
298template <typename T>
299static void
300poisson_cdf_lookup (double lambda, T *p, std::size_t n)
301{
302 double t[TABLESIZE];
303
304 /* Precompute the table for the u up to and including 0.458.
305 * We will almost certainly need it. */
306 int intlambda = static_cast<int> (std::floor (lambda));
307 double P;
308 int tableidx;
309 std::size_t i = n;
310
311 t[0] = P = exp (-lambda);
312 for (tableidx = 1; tableidx <= intlambda; tableidx++)
313 {
314 P = P*lambda/static_cast<double> (tableidx);
315 t[tableidx] = t[tableidx-1] + P;
316 }
317
318 while (i-- > 0)
319 {
320 double u = rand_uniform<double> ();
321
322 /* If u > 0.458 we know we can jump to floor(lambda) before
323 * comparing (this observation is based on Stadlober's winrand
324 * code). For lambda >= 1, this will be a win. Lambda < 1
325 * is already fast, so adding an extra comparison is not a
326 * problem. */
327 int k = (u > 0.458 ? intlambda : 0);
328
329 /* We aren't using a for loop here because when we find the
330 * right k we want to jump to the next iteration of the
331 * outer loop, and the continue statement will only work for
332 * the inner loop. */
333 nextk:
334 if (u <= t[k])
335 {
336 p[i] = static_cast<T> (k);
337 continue;
338 }
339 if (++k < tableidx)
340 goto nextk;
341
342 /* We only need high values of the table very rarely so we
343 * don't automatically compute the entire table. */
344 while (tableidx < TABLESIZE)
345 {
346 P = P*lambda/static_cast<double> (tableidx);
347 t[tableidx] = t[tableidx-1] + P;
348 /* Make sure we converge to 1.0 just in case u is uniform
349 * on [0,1] rather than [0,1). */
350 if (t[tableidx] == t[tableidx-1]) t[tableidx] = 1.0;
351 tableidx++;
352 if (u <= t[tableidx-1]) break;
353 }
354
355 /* We are assuming that the table size is big enough here.
356 * This should be true even if rand_uniform is returning values in
357 * the range [0,1] rather than [0,1). */
358 p[i] = static_cast<T> (tableidx-1);
359 }
360}
361
362/* From Press, et al., Numerical Recipes */
363template <typename T>
364static void
365poisson_rejection (double lambda, T *p, std::size_t n)
366{
367 double sq = std::sqrt (2.0*lambda);
368 double alxm = std::log (lambda);
369 double g = lambda*alxm - xlgamma (lambda+1.0);
370 std::size_t i;
371
372 for (i = 0; i < n; i++)
373 {
374 double y, em, t;
375 do
376 {
377 do
378 {
379 y = tan (M_PI*rand_uniform<double> ());
380 em = sq * y + lambda;
381 }
382 while (em < 0.0);
383 em = std::floor (em);
384 t = 0.9*(1.0+y*y)* exp (em*alxm-flogfak (em)-g);
385 }
386 while (rand_uniform<double> () > t);
387 p[i] = em;
388 }
389}
390
391/* The cutoff of L <= 1e8 in the following two functions before using
392 * the normal approximation is based on:
393 * > L=1e8; x=floor(linspace(0,2*L,1000));
394 * > max(abs(normal_pdf(x,L,L)-poisson_pdf(x,L)))
395 * ans = 1.1376e-28
396 * For L=1e7, the max is around 1e-9, which is within the step size of
397 * rand_uniform. For L>1e10 the pprsc function breaks down, as I saw
398 * from the histogram of a large sample, so 1e8 is both small enough
399 * and large enough. */
400
401/* Generate a set of poisson numbers with the same distribution */
402template <typename T>
403void
405{
406 double L = L_arg;
408 if (L < 0.0 || math::isinf (L))
409 {
410 for (i=0; i<n; i++)
411 p[i] = numeric_limits<T>::NaN ();
412 }
413 else if (L <= 10.0)
414 {
415 poisson_cdf_lookup<T> (L, p, n);
416 }
417 else if (L <= 1e8)
418 {
419 for (i=0; i<n; i++)
420 p[i] = pprsc (L);
421 }
422 else
423 {
424 /* normal approximation: from Phys. Rev. D (1994) v50 p1284 */
425 const double sqrtL = std::sqrt (L);
426 for (i = 0; i < n; i++)
427 {
428 p[i] = std::floor (rand_normal<T> () * sqrtL + L + 0.5);
429 if (p[i] < 0.0)
430 p[i] = 0.0; /* will probably never happen */
431 }
432 }
433}
434
435template void rand_poisson<double> (double, octave_idx_type, double *);
436template void rand_poisson<float> (float, octave_idx_type, float *);
437
438/* Generate one poisson variate */
439template <typename T>
440T
442{
443 double L = L_arg;
444 T ret;
445 if (L < 0.0) ret = numeric_limits<T>::NaN ();
446 else if (L <= 12.0)
447 {
448 /* From Press, et al. Numerical recipes */
449 double g = exp (-L);
450 int em = -1;
451 double t = 1.0;
452 do
453 {
454 ++em;
455 t *= rand_uniform<T> ();
456 }
457 while (t > g);
458 ret = em;
459 }
460 else if (L <= 1e8)
461 {
462 /* numerical recipes */
463 poisson_rejection<T> (L, &ret, 1);
464 }
465 else if (math::isinf (L))
466 {
467 /* FIXME: R uses NaN, but the normal approximation suggests that
468 * limit should be Inf. Which is correct? */
469 ret = numeric_limits<T>::NaN ();
470 }
471 else
472 {
473 /* normal approximation: from Phys. Rev. D (1994) v50 p1284 */
474 ret = std::floor (rand_normal<T> () * std::sqrt (L) + L + 0.5);
475 if (ret < 0.0) ret = 0.0; /* will probably never happen */
476 }
477 return ret;
478}
479
480template OCTAVE_API double rand_poisson<double> (double);
481template OCTAVE_API float rand_poisson<float> (float);
482
483OCTAVE_END_NAMESPACE(octave)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
F77_RET_T const F77_INT const F77_INT const F77_INT const F77_DBLE const F77_DBLE F77_INT F77_DBLE * V
std::complex< T > ceil(const std::complex< T > &x)
Definition lo-mappers.h:103
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
#define OCTAVE_API
Definition main.in.cc:55
double rand_uniform< double >()
Definition randmtzig.cc:442
#define TABLESIZE
#define C5
#define C1
void rand_poisson(T L_arg, octave_idx_type n, T *p)
#define C3
template void rand_poisson< double >(double, octave_idx_type, double *)
template void rand_poisson< float >(float, octave_idx_type, float *)
#define C0
#define C7