RandomLib  1.10
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Pages
MPFRNormalK.hpp
Go to the documentation of this file.
1 /**
2  * \file MPFRNormalK.hpp
3  * \brief Header for MPFRNormalK
4  *
5  * Sampling exactly from the normal distribution for MPFR.
6  *
7  * Copyright (c) Charles Karney (2012) <charles@karney.com> and licensed under
8  * the MIT/X11 License. For more information, see
9  * http://randomlib.sourceforge.net/
10  **********************************************************************/
11 
12 #if !defined(RANDOMLIB_MPFRNORMALK_HPP)
13 #define RANDOMLIB_MPFRNORMALK_HPP 1
14 
15 #include <algorithm> // for max
16 #include <RandomLib/MPFRRandom.hpp>
18 
19 #if HAVE_MPFR || defined(DOXYGEN)
20 
21 namespace RandomLib {
22 
23  /**
24  * \brief The normal distribution for MPFR (Kahn algorithm).
25  *
26  * This class is <b>DEPRECATED</b>. It is included for illustrative purposes
27  * only. The MPFRNormal class provides a somewhat more efficient method for
28  * sampling from the normal distribution.
29  *
30  * Refs:
31  * - H. Kahn, Rand Report RM-1237-AEC, p. 41 (1954).
32  * - M. Abramowitz and I. A. Stegun, p. 953, Sec. 26.8.6.a(4) (1964).
33  * .
34  * N.B. Damien Stehle' drew my attention to this algorithm as a useful way to
35  * compute normal deviates exactly.
36  *
37  * This class uses mutable private objects. So a single MPFRNormalK object
38  * cannot safely be used by multiple threads. In a multi-processing
39  * environment, each thread should use a thread-specific MPFRNormalK object.
40  *
41  * @tparam bits the number of bits in each digit.
42  **********************************************************************/
43  template<int bits = 32> class MPFRNormalK {
44  public:
45 
46  /**
47  * Initialize the MPFRNormalK object.
48  **********************************************************************/
50  { mpfr_init2(_xf, MPFR_PREC_MIN); mpfr_init2(_zf, MPFR_PREC_MIN); }
51  /**
52  * Destroy the MPFRNormalK object.
53  **********************************************************************/
55  { mpfr_clear(_zf); mpfr_clear(_xf); }
56  /**
57  * Sample from the normal distribution with mean 0 and variance 1 returning
58  * a MPFRRandom.
59  *
60  * @param[out] t the MPFRRandom result.
61  * @param[in,out] r a GMP random generator.
62  **********************************************************************/
63  void operator()(MPFRRandom<bits>& t, gmp_randstate_t r) const
64  { Compute(r); _x.swap(t); }
65  /**
66  * Sample from the normal distribution with mean 0 and variance 1.
67  *
68  * @param[out] val the sample from the normal distribution
69  * @param[in,out] r a GMP random generator.
70  * @param[in] round the rounding direction.
71  * @return the MPFR ternary result (&plusmn;1 if val is larger/smaller than
72  * the exact sample).
73  **********************************************************************/
74  int operator()(mpfr_t val, gmp_randstate_t r, mpfr_rnd_t round) const
75  { Compute(r); return _x(val, r, round); }
76  private:
77  // disable copy constructor and assignment operator
78  MPFRNormalK(const MPFRNormalK&);
79  MPFRNormalK& operator=(const MPFRNormalK&);
80  void Compute(gmp_randstate_t r) const {
81  // The algorithm is sample x and z from the exponential distribution; if
82  // (x-1)^2 < 2*z, return (random sign)*x; otherwise repeat. Probability
83  // of acceptance is sqrt(pi/2) * exp(-1/2) = 0.7602.
84  while (true) {
85  _edist(_x, r);
86  _edist(_z, r);
87  for (mp_size_t k = 1; ; ++k) {
88  _x.ExpandTo(r, k - 1);
89  _z.ExpandTo(r, k - 1);
90  mpfr_prec_t prec = (std::max)(mpfr_prec_t(MPFR_PREC_MIN), k * bits);
91  mpfr_set_prec(_xf, prec);
92  mpfr_set_prec(_zf, prec);
93  // Try for acceptance first; so compute upper limit on (y-1)^2 and
94  // lower limit on 2*z.
95  if (_x.UInteger() == 0) {
96  _x(_xf, MPFR_RNDD);
97  mpfr_ui_sub(_xf, 1u, _xf, MPFR_RNDU);
98  } else {
99  _x(_xf, MPFR_RNDU);
100  mpfr_sub_ui(_xf, _xf, 1u, MPFR_RNDU);
101  }
102  mpfr_sqr(_xf, _xf, MPFR_RNDU);
103  _z(_zf, MPFR_RNDD);
104  mpfr_mul_2ui(_zf, _zf, 1u, MPFR_RNDD);
105  if (mpfr_cmp(_xf, _zf) < 0) { // (y-1)^2 < 2*z, so accept
106  if (_x.Boolean(r)) _x.Negate(); // include a random sign
107  return;
108  }
109  // Try for rejection; so compute lower limit on (y-1)^2 and upper
110  // limit on 2*z.
111  if (_x.UInteger() == 0) {
112  _x(_xf, MPFR_RNDU);
113  mpfr_ui_sub(_xf, 1u, _xf, MPFR_RNDD);
114  } else {
115  _x(_xf, MPFR_RNDD);
116  mpfr_sub_ui(_xf, _xf, 1u, MPFR_RNDD);
117  }
118  mpfr_sqr(_xf, _xf, MPFR_RNDD);
119  _z(_zf, MPFR_RNDU);
120  mpfr_mul_2ui(_zf, _zf, 1u, MPFR_RNDU);
121  if (mpfr_cmp(_xf, _zf) > 0) // (y-1)^2 > 2*z, so reject
122  break;
123  // Otherwise repeat with more precision
124  }
125  // Reject and start over with a new y and z
126  }
127  }
128  mutable MPFRRandom<bits> _x;
129  mutable MPFRRandom<bits> _z;
130  mutable mpfr_t _xf;
131  mutable mpfr_t _zf;
132  const MPFRExponential<bits> _edist;
133  };
134 
135 } // namespace RandomLib
136 
137 #endif // HAVE_MPFR
138 #endif // RANDOMLIB_MPFRNORMALK_HPP
The normal distribution for MPFR (Kahn algorithm).
Definition: MPFRNormalK.hpp:43
Header for MPFRExponential.
void operator()(MPFRRandom< bits > &t, gmp_randstate_t r) const
Definition: MPFRNormalK.hpp:63
Handling random numbers in MPFR.
Definition: MPFRRandom.hpp:52
Header for MPFRRandom.
int operator()(mpfr_t val, gmp_randstate_t r, mpfr_rnd_t round) const
Definition: MPFRNormalK.hpp:74