test_exponential_integrals.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import pytest
  2. import numpy as np
  3. from numpy.testing import assert_allclose
  4. import scipy.special as sc
  5. class TestExp1:
  6. def test_branch_cut(self):
  7. assert np.isnan(sc.exp1(-1))
  8. assert sc.exp1(complex(-1, 0)).imag == (
  9. -sc.exp1(complex(-1, -0.0)).imag
  10. )
  11. assert_allclose(
  12. sc.exp1(complex(-1, 0)),
  13. sc.exp1(-1 + 1e-20j),
  14. atol=0,
  15. rtol=1e-15
  16. )
  17. assert_allclose(
  18. sc.exp1(complex(-1, -0.0)),
  19. sc.exp1(-1 - 1e-20j),
  20. atol=0,
  21. rtol=1e-15
  22. )
  23. def test_834(self):
  24. # Regression test for #834
  25. a = sc.exp1(-complex(19.9999990))
  26. b = sc.exp1(-complex(19.9999991))
  27. assert_allclose(a.imag, b.imag, atol=0, rtol=1e-15)
  28. class TestScaledExp1:
  29. @pytest.mark.parametrize('x, expected', [(0, 0), (np.inf, 1)])
  30. def test_limits(self, x, expected):
  31. y = sc._ufuncs._scaled_exp1(x)
  32. assert y == expected
  33. # The expected values were computed with mpmath, e.g.:
  34. #
  35. # from mpmath import mp
  36. # mp.dps = 80
  37. # x = 1e-25
  38. # print(float(x*mp.exp(x)*np.expint(1, x)))
  39. #
  40. # prints 5.698741165994961e-24
  41. #
  42. # The method used to compute _scaled_exp1 changes at x=1
  43. # and x=1250, so values at those inputs, and values just
  44. # above and below them, are included in the test data.
  45. @pytest.mark.parametrize('x, expected',
  46. [(1e-25, 5.698741165994961e-24),
  47. (0.1, 0.20146425447084518),
  48. (0.9995, 0.5962509885831002),
  49. (1.0, 0.5963473623231941),
  50. (1.0005, 0.5964436833238044),
  51. (2.5, 0.7588145912149602),
  52. (10.0, 0.9156333393978808),
  53. (100.0, 0.9901942286733019),
  54. (500.0, 0.9980079523802055),
  55. (1000.0, 0.9990019940238807),
  56. (1249.5, 0.9992009578306811),
  57. (1250.0, 0.9992012769377913),
  58. (1250.25, 0.9992014363957858),
  59. (2000.0, 0.9995004992514963),
  60. (1e4, 0.9999000199940024),
  61. (1e10, 0.9999999999),
  62. (1e15, 0.999999999999999),
  63. ])
  64. def test_scaled_exp1(self, x, expected):
  65. y = sc._ufuncs._scaled_exp1(x)
  66. assert_allclose(y, expected, rtol=2e-15)
  67. class TestExpi:
  68. @pytest.mark.parametrize('result', [
  69. sc.expi(complex(-1, 0)),
  70. sc.expi(complex(-1, -0.0)),
  71. sc.expi(-1)
  72. ])
  73. def test_branch_cut(self, result):
  74. desired = -0.21938393439552027368 # Computed using Mpmath
  75. assert_allclose(result, desired, atol=0, rtol=1e-14)
  76. def test_near_branch_cut(self):
  77. lim_from_above = sc.expi(-1 + 1e-20j)
  78. lim_from_below = sc.expi(-1 - 1e-20j)
  79. assert_allclose(
  80. lim_from_above.real,
  81. lim_from_below.real,
  82. atol=0,
  83. rtol=1e-15
  84. )
  85. assert_allclose(
  86. lim_from_above.imag,
  87. -lim_from_below.imag,
  88. atol=0,
  89. rtol=1e-15
  90. )
  91. def test_continuity_on_positive_real_axis(self):
  92. assert_allclose(
  93. sc.expi(complex(1, 0)),
  94. sc.expi(complex(1, -0.0)),
  95. atol=0,
  96. rtol=1e-15
  97. )
  98. @pytest.mark.parametrize('x, expected', [(0, -np.inf), (np.inf, np.inf)])
  99. def test_limits(self, x, expected):
  100. y = sc.expi(x)
  101. assert y == expected
  102. class TestExpn:
  103. def test_out_of_domain(self):
  104. assert all(np.isnan([sc.expn(-1, 1.0), sc.expn(1, -1.0)]))