_mvt.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import math
  2. import numpy as np
  3. from scipy import special
  4. from scipy.stats._qmc import primes_from_2_to
  5. def _primes(n):
  6. # Defined to facilitate comparison between translation and source
  7. # In Matlab, primes(10.5) -> first four primes, primes(11.5) -> first five
  8. return primes_from_2_to(math.ceil(n))
  9. def _gaminv(a, b):
  10. # Defined to facilitate comparison between translation and source
  11. # Matlab's `gaminv` is like `special.gammaincinv` but args are reversed
  12. return special.gammaincinv(b, a)
  13. def _qsimvtv(m, nu, sigma, a, b, rng):
  14. """Estimates the multivariate t CDF using randomized QMC
  15. Parameters
  16. ----------
  17. m : int
  18. The number of points
  19. nu : float
  20. Degrees of freedom
  21. sigma : ndarray
  22. A 2D positive semidefinite covariance matrix
  23. a : ndarray
  24. Lower integration limits
  25. b : ndarray
  26. Upper integration limits.
  27. rng : Generator
  28. Pseudorandom number generator
  29. Returns
  30. -------
  31. p : float
  32. The estimated CDF.
  33. e : float
  34. An absolute error estimate.
  35. """
  36. # _qsimvtv is a Python translation of the Matlab function qsimvtv,
  37. # semicolons and all.
  38. #
  39. # This function uses an algorithm given in the paper
  40. # "Comparison of Methods for the Numerical Computation of
  41. # Multivariate t Probabilities", in
  42. # J. of Computational and Graphical Stat., 11(2002), pp. 950-971, by
  43. # Alan Genz and Frank Bretz
  44. #
  45. # The primary references for the numerical integration are
  46. # "On a Number-Theoretical Integration Method"
  47. # H. Niederreiter, Aequationes Mathematicae, 8(1972), pp. 304-11.
  48. # and
  49. # "Randomization of Number Theoretic Methods for Multiple Integration"
  50. # R. Cranley & T.N.L. Patterson, SIAM J Numer Anal, 13(1976), pp. 904-14.
  51. #
  52. # Alan Genz is the author of this function and following Matlab functions.
  53. # Alan Genz, WSU Math, PO Box 643113, Pullman, WA 99164-3113
  54. # Email : alangenz@wsu.edu
  55. #
  56. # Copyright (C) 2013, Alan Genz, All rights reserved.
  57. #
  58. # Redistribution and use in source and binary forms, with or without
  59. # modification, are permitted provided the following conditions are met:
  60. # 1. Redistributions of source code must retain the above copyright
  61. # notice, this list of conditions and the following disclaimer.
  62. # 2. Redistributions in binary form must reproduce the above copyright
  63. # notice, this list of conditions and the following disclaimer in
  64. # the documentation and/or other materials provided with the
  65. # distribution.
  66. # 3. The contributor name(s) may not be used to endorse or promote
  67. # products derived from this software without specific prior
  68. # written permission.
  69. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  70. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  71. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  72. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  73. # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  74. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  75. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  76. # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  77. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  78. # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF USE
  79. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  80. # Initialization
  81. sn = max(1, math.sqrt(nu)); ch, az, bz = _chlrps(sigma, a/sn, b/sn)
  82. n = len(sigma); N = 10; P = math.ceil(m/N); on = np.ones(P); p = 0; e = 0
  83. ps = np.sqrt(_primes(5*n*math.log(n+4)/4)); q = ps[:, np.newaxis] # Richtmyer gens.
  84. # Randomization loop for ns samples
  85. c = None; dc = None
  86. for S in range(N):
  87. vp = on.copy(); s = np.zeros((n, P))
  88. for i in range(n):
  89. x = np.abs(2*np.mod(q[i]*np.arange(1, P+1) + rng.random(), 1)-1) # periodizing transform
  90. if i == 0:
  91. r = on
  92. if nu > 0:
  93. r = np.sqrt(2*_gaminv(x, nu/2))
  94. else:
  95. y = _Phinv(c + x*dc)
  96. s[i:] += ch[i:, i-1:i] * y
  97. si = s[i, :]; c = on.copy(); ai = az[i]*r - si; d = on.copy(); bi = bz[i]*r - si
  98. c[ai <= -9] = 0; tl = abs(ai) < 9; c[tl] = _Phi(ai[tl])
  99. d[bi <= -9] = 0; tl = abs(bi) < 9; d[tl] = _Phi(bi[tl])
  100. dc = d - c; vp = vp * dc
  101. d = (np.mean(vp) - p)/(S + 1); p = p + d; e = (S - 1)*e/(S + 1) + d**2
  102. e = math.sqrt(e) # error estimate is 3 times std error with N samples.
  103. return p, e
  104. # Standard statistical normal distribution functions
  105. def _Phi(z):
  106. return special.ndtr(z)
  107. def _Phinv(p):
  108. return special.ndtri(p)
  109. def _chlrps(R, a, b):
  110. """
  111. Computes permuted and scaled lower Cholesky factor c for R which may be
  112. singular, also permuting and scaling integration limit vectors a and b.
  113. """
  114. ep = 1e-10 # singularity tolerance
  115. eps = np.finfo(R.dtype).eps
  116. n = len(R); c = R.copy(); ap = a.copy(); bp = b.copy(); d = np.sqrt(np.maximum(np.diag(c), 0))
  117. for i in range(n):
  118. if d[i] > 0:
  119. c[:, i] /= d[i]; c[i, :] /= d[i]
  120. ap[i] /= d[i]; bp[i] /= d[i]
  121. y = np.zeros((n, 1)); sqtp = math.sqrt(2*math.pi)
  122. for k in range(n):
  123. im = k; ckk = 0; dem = 1; s = 0
  124. for i in range(k, n):
  125. if c[i, i] > eps:
  126. cii = math.sqrt(max(c[i, i], 0))
  127. if i > 0: s = c[i, :k] @ y[:k]
  128. ai = (ap[i]-s)/cii; bi = (bp[i]-s)/cii; de = _Phi(bi)-_Phi(ai)
  129. if de <= dem:
  130. ckk = cii; dem = de; am = ai; bm = bi; im = i
  131. if im > k:
  132. ap[[im, k]] = ap[[k, im]]; bp[[im, k]] = bp[[k, im]]; c[im, im] = c[k, k]
  133. t = c[im, :k].copy(); c[im, :k] = c[k, :k]; c[k, :k] = t
  134. t = c[im+1:, im].copy(); c[im+1:, im] = c[im+1:, k]; c[im+1:, k] = t
  135. t = c[k+1:im, k].copy(); c[k+1:im, k] = c[im, k+1:im].T; c[im, k+1:im] = t.T
  136. if ckk > ep*(k+1):
  137. c[k, k] = ckk; c[k, k+1:] = 0
  138. for i in range(k+1, n):
  139. c[i, k] = c[i, k]/ckk; c[i, k+1:i+1] = c[i, k+1:i+1] - c[i, k]*c[k+1:i+1, k].T
  140. if abs(dem) > ep:
  141. y[k] = (np.exp(-am**2/2) - np.exp(-bm**2/2)) / (sqtp*dem)
  142. else:
  143. y[k] = (am + bm) / 2
  144. if am < -10:
  145. y[k] = bm
  146. elif bm > 10:
  147. y[k] = am
  148. c[k, :k+1] /= ckk; ap[k] /= ckk; bp[k] /= ckk
  149. else:
  150. c[k:, k] = 0; y[k] = (ap[k] + bp[k])/2
  151. pass
  152. return c, ap, bp