xref: /netbsd/external/lgpl3/mpfr/dist/src/nrandom.c (revision 606004a0)
103f29264Smrg /* mpfr_nrandom (rop, state, rnd_mode) -- Generate a normal deviate with mean 0
203f29264Smrg    and variance 1 and round it to the precision of rop according to the given
303f29264Smrg    rounding mode.
403f29264Smrg 
5*606004a0Smrg Copyright 2013-2023 Free Software Foundation, Inc.
603f29264Smrg Contributed by Charles Karney <charles@karney.com>, SRI International.
703f29264Smrg 
803f29264Smrg This file is part of the GNU MPFR Library.
903f29264Smrg 
1003f29264Smrg The GNU MPFR Library is free software; you can redistribute it and/or modify
1103f29264Smrg it under the terms of the GNU Lesser General Public License as published by
1203f29264Smrg the Free Software Foundation; either version 3 of the License, or (at your
1303f29264Smrg option) any later version.
1403f29264Smrg 
1503f29264Smrg The GNU MPFR Library is distributed in the hope that it will be useful, but
1603f29264Smrg WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
1703f29264Smrg or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
1803f29264Smrg License for more details.
1903f29264Smrg 
2003f29264Smrg You should have received a copy of the GNU Lesser General Public License
2103f29264Smrg along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
224da858a9Smrg https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
2303f29264Smrg 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
2403f29264Smrg 
2503f29264Smrg /*
2603f29264Smrg  * Sampling from the normal distribution with zero mean and unit variance.
2703f29264Smrg  * This uses Algorithm N given in:
2803f29264Smrg  *   Charles F. F. Karney,
2903f29264Smrg  *   "Sampling exactly from the normal distribution",
3003f29264Smrg  *   ACM Trans. Math. Software 42(1), 3:1-14 (Jan. 2016).
3103f29264Smrg  *   https://dx.doi.org/10.1145/2710016
32*606004a0Smrg  *   https://arxiv.org/abs/1303.6257
33*606004a0Smrg  *
34*606004a0Smrg  * Note: the algorithm implemented here has been improved in
35*606004a0Smrg  * Du, Fan and Wei in "An improved exact sampling algorithm for the standard
36*606004a0Smrg  * normal distribution", Computational Statistics, 2021,
37*606004a0Smrg  * https://doi.org/10.1007/s00180-021-01136-w
3803f29264Smrg  *
3903f29264Smrg  * The implementation here closely follows the C++ one given in the paper
4003f29264Smrg  * above.  However, here, C is simplified by using gmp_urandomm_ui; the initial
4103f29264Smrg  * rejection step in H just tests the leading bit of p; and the assignment of
4203f29264Smrg  * the sign to the deviate using gmp_urandomb_ui.  Finally, the C++
4303f29264Smrg  * implementation benefits from caching temporary random deviates across calls.
4403f29264Smrg  * This isn't possible in C without additional machinery which would complicate
4503f29264Smrg  * the interface.
4603f29264Smrg  *
4703f29264Smrg  * There are a few "weasel words" regarding the accuracy of this
4803f29264Smrg  * implementation.  The algorithm produces exactly rounded normal deviates
4903f29264Smrg  * provided that gmp's random number engine delivers truly random bits.  If it
5003f29264Smrg  * did, the algorithm would be perfect; however, this implementation would have
5103f29264Smrg  * problems, e.g., in that the integer part of the normal deviate is
5203f29264Smrg  * represented by an unsigned long, whereas in reality the integer part in
5303f29264Smrg  * unbounded.  In this implementation, asserts catch overflow in the integer
5403f29264Smrg  * part and similar (very, very) unlikely events.  In reality, of course, gmp's
5503f29264Smrg  * random number engine has a finite internal state (19937 bits in the case of
5603f29264Smrg  * the MT19937 method).  This means that these unlikely events in fact won't
5703f29264Smrg  * occur.  If the asserts are triggered, then this is an indication that the
5803f29264Smrg  * random number engine is defective.  (Even if a hardware random number
5903f29264Smrg  * generator were used, the most likely explanation for the triggering of the
6003f29264Smrg  * asserts would be that the hardware generator was broken.)
6103f29264Smrg  */
6203f29264Smrg 
6303f29264Smrg #include "random_deviate.h"
6403f29264Smrg 
6503f29264Smrg /* Algorithm H: true with probability exp(-1/2). */
6603f29264Smrg static int
H(gmp_randstate_t r,mpfr_random_deviate_t p,mpfr_random_deviate_t q)6703f29264Smrg H (gmp_randstate_t r, mpfr_random_deviate_t p, mpfr_random_deviate_t q)
6803f29264Smrg {
6903f29264Smrg   /* p and q are temporaries */
7003f29264Smrg   mpfr_random_deviate_reset (p);
7103f29264Smrg   if (mpfr_random_deviate_tstbit (p, 1, r))
7203f29264Smrg     return 1;
7303f29264Smrg   for (;;)
7403f29264Smrg     {
7503f29264Smrg       mpfr_random_deviate_reset (q);
7603f29264Smrg       if (!mpfr_random_deviate_less (q, p, r))
7703f29264Smrg         return 0;
7803f29264Smrg       mpfr_random_deviate_reset (p);
7903f29264Smrg       if (!mpfr_random_deviate_less (p, q, r))
8003f29264Smrg         return 1;
8103f29264Smrg     }
8203f29264Smrg }
8303f29264Smrg 
8403f29264Smrg /* Step N1: return n >= 0 with prob. exp(-n/2) * (1 - exp(-1/2)). */
8503f29264Smrg static unsigned long
G(gmp_randstate_t r,mpfr_random_deviate_t p,mpfr_random_deviate_t q)8603f29264Smrg G (gmp_randstate_t r, mpfr_random_deviate_t p, mpfr_random_deviate_t q)
8703f29264Smrg {
8803f29264Smrg   /* p and q are temporaries */
8903f29264Smrg   unsigned long n = 0;
9003f29264Smrg 
9103f29264Smrg   while (H (r, p, q))
9203f29264Smrg     {
9303f29264Smrg       ++n;
9403f29264Smrg       /* Catch n wrapping around to 0; for a 32-bit unsigned long, the
9503f29264Smrg        * probability of this is exp(-2^30)). */
9603f29264Smrg       MPFR_ASSERTN (n != 0UL);
9703f29264Smrg     }
9803f29264Smrg   return n;
9903f29264Smrg }
10003f29264Smrg 
10103f29264Smrg /* Step N2: true with probability exp(-m*n/2). */
10203f29264Smrg static int
P(unsigned long m,unsigned long n,gmp_randstate_t r,mpfr_random_deviate_t p,mpfr_random_deviate_t q)10303f29264Smrg P (unsigned long m, unsigned long n, gmp_randstate_t r,
10403f29264Smrg    mpfr_random_deviate_t p, mpfr_random_deviate_t q)
10503f29264Smrg {
10603f29264Smrg   /* p and q are temporaries.  m*n is passed as two separate parameters to deal
10703f29264Smrg    * with the case where m*n overflows an unsigned long.  This may be called
10803f29264Smrg    * with m = 0 and n = (unsigned long)(-1) and, because m in handled in to the
10903f29264Smrg    * outer loop, this routine will correctly return 1. */
11003f29264Smrg   while (m--)
11103f29264Smrg     {
11203f29264Smrg       unsigned long k = n;
11303f29264Smrg       while (k--)
11403f29264Smrg         {
11503f29264Smrg           if (!H (r, p, q))
11603f29264Smrg             return 0;
11703f29264Smrg         }
11803f29264Smrg     }
11903f29264Smrg   return 1;
12003f29264Smrg }
12103f29264Smrg 
12203f29264Smrg /* Algorithm C: return (-1, 0, 1) with prob (1/m, 1/m, 1-2/m). */
12303f29264Smrg static int
C(unsigned long m,gmp_randstate_t r)12403f29264Smrg C (unsigned long m, gmp_randstate_t r)
12503f29264Smrg {
12603f29264Smrg   unsigned long n =  gmp_urandomm_ui (r, m);
12703f29264Smrg   return n == 0 ? -1 : (n == 1 ? 0 : 1);
12803f29264Smrg }
12903f29264Smrg 
13003f29264Smrg /* Algorithm B: true with prob exp(-x * (2*k + x) / (2*k + 2)). */
13103f29264Smrg static int
B(unsigned long k,mpfr_random_deviate_t x,gmp_randstate_t r,mpfr_random_deviate_t p,mpfr_random_deviate_t q)13203f29264Smrg B (unsigned long k, mpfr_random_deviate_t x, gmp_randstate_t r,
13303f29264Smrg    mpfr_random_deviate_t p, mpfr_random_deviate_t q)
13403f29264Smrg {
13503f29264Smrg   /* p and q are temporaries */
13603f29264Smrg 
13703f29264Smrg   unsigned long m = 2 * k + 2;
13803f29264Smrg   /* n tracks the parity of the loop; s == 1 on first trip through loop. */
13903f29264Smrg   unsigned n = 0, s = 1;
14003f29264Smrg   int f;
14103f29264Smrg 
14203f29264Smrg   /* Check if 2 * k + 2 would overflow; for a 32-bit unsigned long, the
14303f29264Smrg    * probability of this is exp(-2^61)).  */
14403f29264Smrg   MPFR_ASSERTN (k < ((unsigned long)(-1) >> 1));
14503f29264Smrg 
14603f29264Smrg   for (;; ++n, s = 0)           /* overflow of n is innocuous */
14703f29264Smrg     {
14803f29264Smrg       if ( ((f = k ? 0 : C (m, r)) < 0) ||
14903f29264Smrg            (mpfr_random_deviate_reset (q),
15003f29264Smrg             !mpfr_random_deviate_less (q, s ? x : p, r)) ||
15103f29264Smrg            ((f = k ? C (m, r) : f) < 0) ||
15203f29264Smrg            (f == 0 &&
15303f29264Smrg             (mpfr_random_deviate_reset (p),
15403f29264Smrg              !mpfr_random_deviate_less (p, x, r))) )
15503f29264Smrg         break;
15603f29264Smrg       mpfr_random_deviate_swap (p, q); /* an efficient way of doing p = q */
15703f29264Smrg     }
15803f29264Smrg   return (n & 1U) == 0;
15903f29264Smrg }
16003f29264Smrg 
16103f29264Smrg /* return a normal random deviate with mean 0 and variance 1 as a MPFR  */
16203f29264Smrg int
mpfr_nrandom(mpfr_ptr z,gmp_randstate_t r,mpfr_rnd_t rnd)163*606004a0Smrg mpfr_nrandom (mpfr_ptr z, gmp_randstate_t r, mpfr_rnd_t rnd)
16403f29264Smrg {
16503f29264Smrg   mpfr_random_deviate_t x, p, q;
16603f29264Smrg   int inex;
16703f29264Smrg   unsigned long k, j;
16803f29264Smrg 
16903f29264Smrg   mpfr_random_deviate_init (x);
17003f29264Smrg   mpfr_random_deviate_init (p);
17103f29264Smrg   mpfr_random_deviate_init (q);
17203f29264Smrg   for (;;)
17303f29264Smrg     {
17403f29264Smrg       k = G (r, p, q);                               /* step 1 */
17503f29264Smrg       if (!P (k, k - 1, r, p, q))
17603f29264Smrg         continue;                                    /* step 2 */
17703f29264Smrg       mpfr_random_deviate_reset (x);                 /* step 3 */
17803f29264Smrg       for (j = 0; j <= k && B (k, x, r, p, q); ++j); /* step 4 */
17903f29264Smrg       if (j > k)
18003f29264Smrg         break;
18103f29264Smrg     }
18203f29264Smrg   mpfr_random_deviate_clear (q);
18303f29264Smrg   mpfr_random_deviate_clear (p);
18403f29264Smrg   /* steps 5, 6, 7 */
18503f29264Smrg   inex = mpfr_random_deviate_value (gmp_urandomb_ui (r, 1), k, x, z, r, rnd);
18603f29264Smrg   mpfr_random_deviate_clear (x);
18703f29264Smrg   return inex;
18803f29264Smrg }
189