Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/stats/_rvs_sampling.py: 11%

27 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1# -*- coding: utf-8 -*- 

2import numpy as np 

3from scipy._lib._util import check_random_state 

4 

5 

6def rvs_ratio_uniforms(pdf, umax, vmin, vmax, size=1, c=0, random_state=None): 

7 """ 

8 Generate random samples from a probability density function using the 

9 ratio-of-uniforms method. 

10 

11 Parameters 

12 ---------- 

13 pdf : callable 

14 A function with signature `pdf(x)` that is proportional to the 

15 probability density function of the distribution. 

16 umax : float 

17 The upper bound of the bounding rectangle in the u-direction. 

18 vmin : float 

19 The lower bound of the bounding rectangle in the v-direction. 

20 vmax : float 

21 The upper bound of the bounding rectangle in the v-direction. 

22 size : int or tuple of ints, optional 

23 Defining number of random variates (default is 1). 

24 c : float, optional. 

25 Shift parameter of ratio-of-uniforms method, see Notes. Default is 0. 

26 random_state : {None, int, `numpy.random.Generator`, 

27 `numpy.random.RandomState`}, optional 

28 

29 If `seed` is None (or `np.random`), the `numpy.random.RandomState` 

30 singleton is used. 

31 If `seed` is an int, a new ``RandomState`` instance is used, 

32 seeded with `seed`. 

33 If `seed` is already a ``Generator`` or ``RandomState`` instance then 

34 that instance is used. 

35 

36 Returns 

37 ------- 

38 rvs : ndarray 

39 The random variates distributed according to the probability 

40 distribution defined by the pdf. 

41 

42 Notes 

43 ----- 

44 Given a univariate probability density function `pdf` and a constant `c`, 

45 define the set ``A = {(u, v) : 0 < u <= sqrt(pdf(v/u + c))}``. 

46 If `(U, V)` is a random vector uniformly distributed over `A`, 

47 then `V/U + c` follows a distribution according to `pdf`. 

48 

49 The above result (see [1]_, [2]_) can be used to sample random variables 

50 using only the pdf, i.e. no inversion of the cdf is required. Typical 

51 choices of `c` are zero or the mode of `pdf`. The set `A` is a subset of 

52 the rectangle ``R = [0, umax] x [vmin, vmax]`` where 

53 

54 - ``umax = sup sqrt(pdf(x))`` 

55 - ``vmin = inf (x - c) sqrt(pdf(x))`` 

56 - ``vmax = sup (x - c) sqrt(pdf(x))`` 

57 

58 In particular, these values are finite if `pdf` is bounded and 

59 ``x**2 * pdf(x)`` is bounded (i.e. subquadratic tails). 

60 One can generate `(U, V)` uniformly on `R` and return 

61 `V/U + c` if `(U, V)` are also in `A` which can be directly 

62 verified. 

63 

64 The algorithm is not changed if one replaces `pdf` by k * `pdf` for any 

65 constant k > 0. Thus, it is often convenient to work with a function 

66 that is proportional to the probability density function by dropping 

67 unneccessary normalization factors. 

68 

69 Intuitively, the method works well if `A` fills up most of the 

70 enclosing rectangle such that the probability is high that `(U, V)` 

71 lies in `A` whenever it lies in `R` as the number of required 

72 iterations becomes too large otherwise. To be more precise, note that 

73 the expected number of iterations to draw `(U, V)` uniformly 

74 distributed on `R` such that `(U, V)` is also in `A` is given by 

75 the ratio ``area(R) / area(A) = 2 * umax * (vmax - vmin) / area(pdf)``, 

76 where `area(pdf)` is the integral of `pdf` (which is equal to one if the 

77 probability density function is used but can take on other values if a 

78 function proportional to the density is used). The equality holds since 

79 the area of `A` is equal to 0.5 * area(pdf) (Theorem 7.1 in [1]_). 

80 If the sampling fails to generate a single random variate after 50000 

81 iterations (i.e. not a single draw is in `A`), an exception is raised. 

82 

83 If the bounding rectangle is not correctly specified (i.e. if it does not 

84 contain `A`), the algorithm samples from a distribution different from 

85 the one given by `pdf`. It is therefore recommended to perform a 

86 test such as `~scipy.stats.kstest` as a check. 

87 

88 References 

89 ---------- 

90 .. [1] L. Devroye, "Non-Uniform Random Variate Generation", 

91 Springer-Verlag, 1986. 

92 

93 .. [2] W. Hoermann and J. Leydold, "Generating generalized inverse Gaussian 

94 random variates", Statistics and Computing, 24(4), p. 547--557, 2014. 

95 

96 .. [3] A.J. Kinderman and J.F. Monahan, "Computer Generation of Random 

97 Variables Using the Ratio of Uniform Deviates", 

98 ACM Transactions on Mathematical Software, 3(3), p. 257--260, 1977. 

99 

100 Examples 

101 -------- 

102 >>> import numpy as np 

103 >>> from scipy import stats 

104 >>> rng = np.random.default_rng() 

105 

106 Simulate normally distributed random variables. It is easy to compute the 

107 bounding rectangle explicitly in that case. For simplicity, we drop the 

108 normalization factor of the density. 

109 

110 >>> f = lambda x: np.exp(-x**2 / 2) 

111 >>> v_bound = np.sqrt(f(np.sqrt(2))) * np.sqrt(2) 

112 >>> umax, vmin, vmax = np.sqrt(f(0)), -v_bound, v_bound 

113 >>> rvs = stats.rvs_ratio_uniforms(f, umax, vmin, vmax, size=2500, 

114 ... random_state=rng) 

115 

116 The K-S test confirms that the random variates are indeed normally 

117 distributed (normality is not rejected at 5% significance level): 

118 

119 >>> stats.kstest(rvs, 'norm')[1] 

120 0.250634764150542 

121 

122 The exponential distribution provides another example where the bounding 

123 rectangle can be determined explicitly. 

124 

125 >>> rvs = stats.rvs_ratio_uniforms(lambda x: np.exp(-x), umax=1, 

126 ... vmin=0, vmax=2*np.exp(-1), size=1000, 

127 ... random_state=rng) 

128 >>> stats.kstest(rvs, 'expon')[1] 

129 0.21121052054580314 

130 

131 """ 

132 if vmin >= vmax: 

133 raise ValueError("vmin must be smaller than vmax.") 

134 

135 if umax <= 0: 

136 raise ValueError("umax must be positive.") 

137 

138 size1d = tuple(np.atleast_1d(size)) 

139 N = np.prod(size1d) # number of rvs needed, reshape upon return 

140 

141 # start sampling using ratio of uniforms method 

142 rng = check_random_state(random_state) 

143 x = np.zeros(N) 

144 simulated, i = 0, 1 

145 

146 # loop until N rvs have been generated: expected runtime is finite. 

147 # to avoid infinite loop, raise exception if not a single rv has been 

148 # generated after 50000 tries. even if the expected numer of iterations 

149 # is 1000, the probability of this event is (1-1/1000)**50000 

150 # which is of order 10e-22 

151 while simulated < N: 

152 k = N - simulated 

153 # simulate uniform rvs on [0, umax] and [vmin, vmax] 

154 u1 = umax * rng.uniform(size=k) 

155 v1 = rng.uniform(vmin, vmax, size=k) 

156 # apply rejection method 

157 rvs = v1 / u1 + c 

158 accept = (u1**2 <= pdf(rvs)) 

159 num_accept = np.sum(accept) 

160 if num_accept > 0: 

161 x[simulated:(simulated + num_accept)] = rvs[accept] 

162 simulated += num_accept 

163 

164 if (simulated == 0) and (i*N >= 50000): 

165 msg = ("Not a single random variate could be generated in {} " 

166 "attempts. The ratio of uniforms method does not appear " 

167 "to work for the provided parameters. Please check the " 

168 "pdf and the bounds.".format(i*N)) 

169 raise RuntimeError(msg) 

170 i += 1 

171 

172 return np.reshape(x, size1d)