wright_bessel_data.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. """Compute a grid of values for Wright's generalized Bessel function
  2. and save the values to data files for use in tests. Using mpmath directly in
  3. tests would take too long.
  4. This takes about 10 minutes to run on a 2.7 GHz i7 Macbook Pro.
  5. """
  6. from functools import lru_cache
  7. import os
  8. from time import time
  9. import numpy as np
  10. from scipy.special._mptestutils import mpf2float
  11. try:
  12. import mpmath as mp
  13. except ImportError:
  14. pass
  15. # exp_inf: smallest value x for which exp(x) == inf
  16. exp_inf = 709.78271289338403
  17. # 64 Byte per value
  18. @lru_cache(maxsize=100_000)
  19. def rgamma_cached(x, dps):
  20. with mp.workdps(dps):
  21. return mp.rgamma(x)
  22. def mp_wright_bessel(a, b, x, dps=50, maxterms=2000):
  23. """Compute Wright's generalized Bessel function as Series with mpmath.
  24. """
  25. with mp.workdps(dps):
  26. a, b, x = mp.mpf(a), mp.mpf(b), mp.mpf(x)
  27. res = mp.nsum(lambda k: x**k / mp.fac(k)
  28. * rgamma_cached(a * k + b, dps=dps),
  29. [0, mp.inf],
  30. tol=dps, method='s', steps=[maxterms]
  31. )
  32. return mpf2float(res)
  33. def main():
  34. t0 = time()
  35. print(__doc__)
  36. pwd = os.path.dirname(__file__)
  37. eps = np.finfo(float).eps * 100
  38. a_range = np.array([eps,
  39. 1e-4 * (1 - eps), 1e-4, 1e-4 * (1 + eps),
  40. 1e-3 * (1 - eps), 1e-3, 1e-3 * (1 + eps),
  41. 0.1, 0.5,
  42. 1 * (1 - eps), 1, 1 * (1 + eps),
  43. 1.5, 2, 4.999, 5, 10])
  44. b_range = np.array([0, eps, 1e-10, 1e-5, 0.1, 1, 2, 10, 20, 100])
  45. x_range = np.array([0, eps, 1 - eps, 1, 1 + eps,
  46. 1.5,
  47. 2 - eps, 2, 2 + eps,
  48. 9 - eps, 9, 9 + eps,
  49. 10 * (1 - eps), 10, 10 * (1 + eps),
  50. 100 * (1 - eps), 100, 100 * (1 + eps),
  51. 500, exp_inf, 1e3, 1e5, 1e10, 1e20])
  52. a_range, b_range, x_range = np.meshgrid(a_range, b_range, x_range,
  53. indexing='ij')
  54. a_range = a_range.flatten()
  55. b_range = b_range.flatten()
  56. x_range = x_range.flatten()
  57. # filter out some values, especially too large x
  58. bool_filter = ~((a_range < 5e-3) & (x_range >= exp_inf))
  59. bool_filter = bool_filter & ~((a_range < 0.2) & (x_range > exp_inf))
  60. bool_filter = bool_filter & ~((a_range < 0.5) & (x_range > 1e3))
  61. bool_filter = bool_filter & ~((a_range < 0.56) & (x_range > 5e3))
  62. bool_filter = bool_filter & ~((a_range < 1) & (x_range > 1e4))
  63. bool_filter = bool_filter & ~((a_range < 1.4) & (x_range > 1e5))
  64. bool_filter = bool_filter & ~((a_range < 1.8) & (x_range > 1e6))
  65. bool_filter = bool_filter & ~((a_range < 2.2) & (x_range > 1e7))
  66. bool_filter = bool_filter & ~((a_range < 2.5) & (x_range > 1e8))
  67. bool_filter = bool_filter & ~((a_range < 2.9) & (x_range > 1e9))
  68. bool_filter = bool_filter & ~((a_range < 3.3) & (x_range > 1e10))
  69. bool_filter = bool_filter & ~((a_range < 3.7) & (x_range > 1e11))
  70. bool_filter = bool_filter & ~((a_range < 4) & (x_range > 1e12))
  71. bool_filter = bool_filter & ~((a_range < 4.4) & (x_range > 1e13))
  72. bool_filter = bool_filter & ~((a_range < 4.7) & (x_range > 1e14))
  73. bool_filter = bool_filter & ~((a_range < 5.1) & (x_range > 1e15))
  74. bool_filter = bool_filter & ~((a_range < 5.4) & (x_range > 1e16))
  75. bool_filter = bool_filter & ~((a_range < 5.8) & (x_range > 1e17))
  76. bool_filter = bool_filter & ~((a_range < 6.2) & (x_range > 1e18))
  77. bool_filter = bool_filter & ~((a_range < 6.2) & (x_range > 1e18))
  78. bool_filter = bool_filter & ~((a_range < 6.5) & (x_range > 1e19))
  79. bool_filter = bool_filter & ~((a_range < 6.9) & (x_range > 1e20))
  80. # filter out known values that do not meet the required numerical accuracy
  81. # see test test_wright_data_grid_failures
  82. failing = np.array([
  83. [0.1, 100, 709.7827128933841],
  84. [0.5, 10, 709.7827128933841],
  85. [0.5, 10, 1000],
  86. [0.5, 100, 1000],
  87. [1, 20, 100000],
  88. [1, 100, 100000],
  89. [1.0000000000000222, 20, 100000],
  90. [1.0000000000000222, 100, 100000],
  91. [1.5, 0, 500],
  92. [1.5, 2.220446049250313e-14, 500],
  93. [1.5, 1.e-10, 500],
  94. [1.5, 1.e-05, 500],
  95. [1.5, 0.1, 500],
  96. [1.5, 20, 100000],
  97. [1.5, 100, 100000],
  98. ]).tolist()
  99. does_fail = np.full_like(a_range, False, dtype=bool)
  100. for i in range(x_range.size):
  101. if [a_range[i], b_range[i], x_range[i]] in failing:
  102. does_fail[i] = True
  103. # filter and flatten
  104. a_range = a_range[bool_filter]
  105. b_range = b_range[bool_filter]
  106. x_range = x_range[bool_filter]
  107. does_fail = does_fail[bool_filter]
  108. dataset = []
  109. print(f"Computing {x_range.size} single points.")
  110. print("Tests will fail for the following data points:")
  111. for i in range(x_range.size):
  112. a = a_range[i]
  113. b = b_range[i]
  114. x = x_range[i]
  115. # take care of difficult corner cases
  116. maxterms = 1000
  117. if a < 1e-6 and x >= exp_inf/10:
  118. maxterms = 2000
  119. f = mp_wright_bessel(a, b, x, maxterms=maxterms)
  120. if does_fail[i]:
  121. print("failing data point a, b, x, value = "
  122. f"[{a}, {b}, {x}, {f}]")
  123. else:
  124. dataset.append((a, b, x, f))
  125. dataset = np.array(dataset)
  126. filename = os.path.join(pwd, '..', 'tests', 'data', 'local',
  127. 'wright_bessel.txt')
  128. np.savetxt(filename, dataset)
  129. print("{:.1f} minutes elapsed".format((time() - t0)/60))
  130. if __name__ == "__main__":
  131. main()