gammainc_asy.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """
  2. Precompute coefficients of Temme's asymptotic expansion for gammainc.
  3. This takes about 8 hours to run on a 2.3 GHz Macbook Pro with 4GB ram.
  4. Sources:
  5. [1] NIST, "Digital Library of Mathematical Functions",
  6. https://dlmf.nist.gov/
  7. """
  8. import os
  9. from scipy.special._precompute.utils import lagrange_inversion
  10. try:
  11. import mpmath as mp
  12. except ImportError:
  13. pass
  14. def compute_a(n):
  15. """a_k from DLMF 5.11.6"""
  16. a = [mp.sqrt(2)/2]
  17. for k in range(1, n):
  18. ak = a[-1]/k
  19. for j in range(1, len(a)):
  20. ak -= a[j]*a[-j]/(j + 1)
  21. ak /= a[0]*(1 + mp.mpf(1)/(k + 1))
  22. a.append(ak)
  23. return a
  24. def compute_g(n):
  25. """g_k from DLMF 5.11.3/5.11.5"""
  26. a = compute_a(2*n)
  27. g = [mp.sqrt(2)*mp.rf(0.5, k)*a[2*k] for k in range(n)]
  28. return g
  29. def eta(lam):
  30. """Function from DLMF 8.12.1 shifted to be centered at 0."""
  31. if lam > 0:
  32. return mp.sqrt(2*(lam - mp.log(lam + 1)))
  33. elif lam < 0:
  34. return -mp.sqrt(2*(lam - mp.log(lam + 1)))
  35. else:
  36. return 0
  37. def compute_alpha(n):
  38. """alpha_n from DLMF 8.12.13"""
  39. coeffs = mp.taylor(eta, 0, n - 1)
  40. return lagrange_inversion(coeffs)
  41. def compute_d(K, N):
  42. """d_{k, n} from DLMF 8.12.12"""
  43. M = N + 2*K
  44. d0 = [-mp.mpf(1)/3]
  45. alpha = compute_alpha(M + 2)
  46. for n in range(1, M):
  47. d0.append((n + 2)*alpha[n+2])
  48. d = [d0]
  49. g = compute_g(K)
  50. for k in range(1, K):
  51. dk = []
  52. for n in range(M - 2*k):
  53. dk.append((-1)**k*g[k]*d[0][n] + (n + 2)*d[k-1][n+2])
  54. d.append(dk)
  55. for k in range(K):
  56. d[k] = d[k][:N]
  57. return d
  58. header = \
  59. r"""/* This file was automatically generated by _precomp/gammainc.py.
  60. * Do not edit it manually!
  61. */
  62. #ifndef IGAM_H
  63. #define IGAM_H
  64. #define K {}
  65. #define N {}
  66. static const double d[K][N] =
  67. {{"""
  68. footer = \
  69. r"""
  70. #endif
  71. """
  72. def main():
  73. print(__doc__)
  74. K = 25
  75. N = 25
  76. with mp.workdps(50):
  77. d = compute_d(K, N)
  78. fn = os.path.join(os.path.dirname(__file__), '..', 'cephes', 'igam.h')
  79. with open(fn + '.new', 'w') as f:
  80. f.write(header.format(K, N))
  81. for k, row in enumerate(d):
  82. row = [mp.nstr(x, 17, min_fixed=0, max_fixed=0) for x in row]
  83. f.write('{')
  84. f.write(", ".join(row))
  85. if k < K - 1:
  86. f.write('},\n')
  87. else:
  88. f.write('}};\n')
  89. f.write(footer)
  90. os.rename(fn + '.new', fn)
  91. if __name__ == "__main__":
  92. main()