test_polynomial.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. import pytest
  2. import numpy as np
  3. import numpy.polynomial.polynomial as poly
  4. from numpy.testing import (
  5. assert_,
  6. assert_allclose,
  7. assert_almost_equal,
  8. assert_array_almost_equal,
  9. assert_array_equal,
  10. assert_equal,
  11. assert_raises,
  12. )
  13. # `poly1d` has some support for `np.bool` and `np.timedelta64`,
  14. # but it is limited and they are therefore excluded here
  15. TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O"
  16. class TestPolynomial:
  17. def test_poly1d_str_and_repr(self):
  18. p = np.poly1d([1., 2, 3])
  19. assert_equal(repr(p), 'poly1d([1., 2., 3.])')
  20. assert_equal(str(p),
  21. ' 2\n'
  22. '1 x + 2 x + 3')
  23. q = np.poly1d([3., 2, 1])
  24. assert_equal(repr(q), 'poly1d([3., 2., 1.])')
  25. assert_equal(str(q),
  26. ' 2\n'
  27. '3 x + 2 x + 1')
  28. r = np.poly1d([1.89999 + 2j, -3j, -5.12345678, 2 + 1j])
  29. assert_equal(str(r),
  30. ' 3 2\n'
  31. '(1.9 + 2j) x - 3j x - 5.123 x + (2 + 1j)')
  32. assert_equal(str(np.poly1d([-3, -2, -1])),
  33. ' 2\n'
  34. '-3 x - 2 x - 1')
  35. def test_poly1d_resolution(self):
  36. p = np.poly1d([1., 2, 3])
  37. q = np.poly1d([3., 2, 1])
  38. assert_equal(p(0), 3.0)
  39. assert_equal(p(5), 38.0)
  40. assert_equal(q(0), 1.0)
  41. assert_equal(q(5), 86.0)
  42. def test_poly1d_math(self):
  43. # here we use some simple coeffs to make calculations easier
  44. p = np.poly1d([1., 2, 4])
  45. q = np.poly1d([4., 2, 1])
  46. assert_equal(p / q, (np.poly1d([0.25]), np.poly1d([1.5, 3.75])))
  47. assert_equal(p.integ(), np.poly1d([1 / 3, 1., 4., 0.]))
  48. assert_equal(p.integ(1), np.poly1d([1 / 3, 1., 4., 0.]))
  49. p = np.poly1d([1., 2, 3])
  50. q = np.poly1d([3., 2, 1])
  51. assert_equal(p * q, np.poly1d([3., 8., 14., 8., 3.]))
  52. assert_equal(p + q, np.poly1d([4., 4., 4.]))
  53. assert_equal(p - q, np.poly1d([-2., 0., 2.]))
  54. assert_equal(p ** 4, np.poly1d([1., 8., 36., 104., 214.,
  55. 312., 324., 216., 81.]))
  56. assert_equal(p(q), np.poly1d([9., 12., 16., 8., 6.]))
  57. assert_equal(q(p), np.poly1d([3., 12., 32., 40., 34.]))
  58. assert_equal(p.deriv(), np.poly1d([2., 2.]))
  59. assert_equal(p.deriv(2), np.poly1d([2.]))
  60. assert_equal(np.polydiv(np.poly1d([1, 0, -1]), np.poly1d([1, 1])),
  61. (np.poly1d([1., -1.]), np.poly1d([0.])))
  62. @pytest.mark.parametrize("type_code", TYPE_CODES)
  63. def test_poly1d_misc(self, type_code: str) -> None:
  64. dtype = np.dtype(type_code)
  65. ar = np.array([1, 2, 3], dtype=dtype)
  66. p = np.poly1d(ar)
  67. # `__eq__`
  68. assert_equal(np.asarray(p), ar)
  69. assert_equal(np.asarray(p).dtype, dtype)
  70. assert_equal(len(p), 2)
  71. # `__getitem__`
  72. comparison_dct = {-1: 0, 0: 3, 1: 2, 2: 1, 3: 0}
  73. for index, ref in comparison_dct.items():
  74. scalar = p[index]
  75. assert_equal(scalar, ref)
  76. if dtype == np.object_:
  77. assert isinstance(scalar, int)
  78. else:
  79. assert_equal(scalar.dtype, dtype)
  80. def test_poly1d_variable_arg(self):
  81. q = np.poly1d([1., 2, 3], variable='y')
  82. assert_equal(str(q),
  83. ' 2\n'
  84. '1 y + 2 y + 3')
  85. q = np.poly1d([1., 2, 3], variable='lambda')
  86. assert_equal(str(q),
  87. ' 2\n'
  88. '1 lambda + 2 lambda + 3')
  89. def test_poly(self):
  90. assert_array_almost_equal(np.poly([3, -np.sqrt(2), np.sqrt(2)]),
  91. [1, -3, -2, 6])
  92. # From matlab docs
  93. A = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
  94. assert_array_almost_equal(np.poly(A), [1, -6, -72, -27])
  95. # Should produce real output for perfect conjugates
  96. assert_(np.isrealobj(np.poly([+1.082j, +2.613j, -2.613j, -1.082j])))
  97. assert_(np.isrealobj(np.poly([0 + 1j, -0 + -1j, 1 + 2j,
  98. 1 - 2j, 1. + 3.5j, 1 - 3.5j])))
  99. assert_(np.isrealobj(np.poly([1j, -1j, 1 + 2j, 1 - 2j, 1 + 3j, 1 - 3.j])))
  100. assert_(np.isrealobj(np.poly([1j, -1j, 1 + 2j, 1 - 2j])))
  101. assert_(np.isrealobj(np.poly([1j, -1j, 2j, -2j])))
  102. assert_(np.isrealobj(np.poly([1j, -1j])))
  103. assert_(np.isrealobj(np.poly([1, -1])))
  104. assert_(np.iscomplexobj(np.poly([1j, -1.0000001j])))
  105. np.random.seed(42)
  106. a = np.random.randn(100) + 1j * np.random.randn(100)
  107. assert_(np.isrealobj(np.poly(np.concatenate((a, np.conjugate(a))))))
  108. def test_roots(self):
  109. assert_array_equal(np.roots([1, 0, 0]), [0, 0])
  110. # Testing for larger root values
  111. for i in np.logspace(10, 25, num=1000, base=10):
  112. tgt = np.array([-1, 1, i])
  113. res = np.sort(np.roots(poly.polyfromroots(tgt)[::-1]))
  114. # Adapting the expected precision according to the root value,
  115. # to take into account numerical calculation error
  116. assert_almost_equal(res, tgt, 14 - int(np.log10(i)))
  117. for i in np.logspace(10, 25, num=1000, base=10):
  118. tgt = np.array([-1, 1.01, i])
  119. res = np.sort(np.roots(poly.polyfromroots(tgt)[::-1]))
  120. # Adapting the expected precision according to the root value,
  121. # to take into account numerical calculation error
  122. assert_almost_equal(res, tgt, 14 - int(np.log10(i)))
  123. def test_str_leading_zeros(self):
  124. p = np.poly1d([4, 3, 2, 1])
  125. p[3] = 0
  126. assert_equal(str(p),
  127. " 2\n"
  128. "3 x + 2 x + 1")
  129. p = np.poly1d([1, 2])
  130. p[0] = 0
  131. p[1] = 0
  132. assert_equal(str(p), " \n0")
  133. def test_polyfit(self):
  134. c = np.array([3., 2., 1.])
  135. x = np.linspace(0, 2, 7)
  136. y = np.polyval(c, x)
  137. err = [1, -1, 1, -1, 1, -1, 1]
  138. weights = np.arange(8, 1, -1)**2 / 7.0
  139. # Check exception when too few points for variance estimate. Note that
  140. # the estimate requires the number of data points to exceed
  141. # degree + 1
  142. assert_raises(ValueError, np.polyfit,
  143. [1], [1], deg=0, cov=True)
  144. # check 1D case
  145. m, cov = np.polyfit(x, y + err, 2, cov=True)
  146. est = [3.8571, 0.2857, 1.619]
  147. assert_almost_equal(est, m, decimal=4)
  148. val0 = [[ 1.4694, -2.9388, 0.8163],
  149. [-2.9388, 6.3673, -2.1224],
  150. [ 0.8163, -2.1224, 1.161 ]] # noqa: E202
  151. assert_almost_equal(val0, cov, decimal=4)
  152. m2, cov2 = np.polyfit(x, y + err, 2, w=weights, cov=True)
  153. assert_almost_equal([4.8927, -1.0177, 1.7768], m2, decimal=4)
  154. val = [[ 4.3964, -5.0052, 0.4878],
  155. [-5.0052, 6.8067, -0.9089],
  156. [ 0.4878, -0.9089, 0.3337]]
  157. assert_almost_equal(val, cov2, decimal=4)
  158. m3, cov3 = np.polyfit(x, y + err, 2, w=weights, cov="unscaled")
  159. assert_almost_equal([4.8927, -1.0177, 1.7768], m3, decimal=4)
  160. val = [[ 0.1473, -0.1677, 0.0163],
  161. [-0.1677, 0.228 , -0.0304], # noqa: E203
  162. [ 0.0163, -0.0304, 0.0112]]
  163. assert_almost_equal(val, cov3, decimal=4)
  164. # check 2D (n,1) case
  165. y = y[:, np.newaxis]
  166. c = c[:, np.newaxis]
  167. assert_almost_equal(c, np.polyfit(x, y, 2))
  168. # check 2D (n,2) case
  169. yy = np.concatenate((y, y), axis=1)
  170. cc = np.concatenate((c, c), axis=1)
  171. assert_almost_equal(cc, np.polyfit(x, yy, 2))
  172. m, cov = np.polyfit(x, yy + np.array(err)[:, np.newaxis], 2, cov=True)
  173. assert_almost_equal(est, m[:, 0], decimal=4)
  174. assert_almost_equal(est, m[:, 1], decimal=4)
  175. assert_almost_equal(val0, cov[:, :, 0], decimal=4)
  176. assert_almost_equal(val0, cov[:, :, 1], decimal=4)
  177. # check order 1 (deg=0) case, were the analytic results are simple
  178. np.random.seed(123)
  179. y = np.random.normal(size=(4, 10000))
  180. mean, cov = np.polyfit(np.zeros(y.shape[0]), y, deg=0, cov=True)
  181. # Should get sigma_mean = sigma/sqrt(N) = 1./sqrt(4) = 0.5.
  182. assert_allclose(mean.std(), 0.5, atol=0.01)
  183. assert_allclose(np.sqrt(cov.mean()), 0.5, atol=0.01)
  184. # Without scaling, since reduced chi2 is 1, the result should be the same.
  185. mean, cov = np.polyfit(np.zeros(y.shape[0]), y, w=np.ones(y.shape[0]),
  186. deg=0, cov="unscaled")
  187. assert_allclose(mean.std(), 0.5, atol=0.01)
  188. assert_almost_equal(np.sqrt(cov.mean()), 0.5)
  189. # If we estimate our errors wrong, no change with scaling:
  190. w = np.full(y.shape[0], 1. / 0.5)
  191. mean, cov = np.polyfit(np.zeros(y.shape[0]), y, w=w, deg=0, cov=True)
  192. assert_allclose(mean.std(), 0.5, atol=0.01)
  193. assert_allclose(np.sqrt(cov.mean()), 0.5, atol=0.01)
  194. # But if we do not scale, our estimate for the error in the mean will
  195. # differ.
  196. mean, cov = np.polyfit(np.zeros(y.shape[0]), y, w=w, deg=0, cov="unscaled")
  197. assert_allclose(mean.std(), 0.5, atol=0.01)
  198. assert_almost_equal(np.sqrt(cov.mean()), 0.25)
  199. def test_objects(self):
  200. from decimal import Decimal
  201. p = np.poly1d([Decimal('4.0'), Decimal('3.0'), Decimal('2.0')])
  202. p2 = p * Decimal('1.333333333333333')
  203. assert_(p2[1] == Decimal("3.9999999999999990"))
  204. p2 = p.deriv()
  205. assert_(p2[1] == Decimal('8.0'))
  206. p2 = p.integ()
  207. assert_(p2[3] == Decimal("1.333333333333333333333333333"))
  208. assert_(p2[2] == Decimal('1.5'))
  209. assert_(np.issubdtype(p2.coeffs.dtype, np.object_))
  210. p = np.poly([Decimal(1), Decimal(2)])
  211. assert_equal(np.poly([Decimal(1), Decimal(2)]),
  212. [1, Decimal(-3), Decimal(2)])
  213. def test_complex(self):
  214. p = np.poly1d([3j, 2j, 1j])
  215. p2 = p.integ()
  216. assert_((p2.coeffs == [1j, 1j, 1j, 0]).all())
  217. p2 = p.deriv()
  218. assert_((p2.coeffs == [6j, 2j]).all())
  219. def test_integ_coeffs(self):
  220. p = np.poly1d([3, 2, 1])
  221. p2 = p.integ(3, k=[9, 7, 6])
  222. expected = [1 / 4 / 5, 1 / 3 / 4, 1 / 2 / 3, 9 / 1 / 2, 7, 6]
  223. assert_((p2.coeffs == expected).all())
  224. def test_zero_dims(self):
  225. try:
  226. np.poly(np.zeros((0, 0)))
  227. except ValueError:
  228. pass
  229. def test_poly_int_overflow(self):
  230. """
  231. Regression test for gh-5096.
  232. """
  233. v = np.arange(1, 21)
  234. assert_almost_equal(np.poly(v), np.poly(np.diag(v)))
  235. def test_zero_poly_dtype(self):
  236. """
  237. Regression test for gh-16354.
  238. """
  239. z = np.array([0, 0, 0])
  240. p = np.poly1d(z.astype(np.int64))
  241. assert_equal(p.coeffs.dtype, np.int64)
  242. p = np.poly1d(z.astype(np.float32))
  243. assert_equal(p.coeffs.dtype, np.float32)
  244. p = np.poly1d(z.astype(np.complex64))
  245. assert_equal(p.coeffs.dtype, np.complex64)
  246. def test_poly_eq(self):
  247. p = np.poly1d([1, 2, 3])
  248. p2 = np.poly1d([1, 2, 4])
  249. assert_equal(p == None, False) # noqa: E711
  250. assert_equal(p != None, True) # noqa: E711
  251. assert_equal(p == p, True)
  252. assert_equal(p == p2, False)
  253. assert_equal(p != p2, True)
  254. def test_polydiv(self):
  255. b = np.poly1d([2, 6, 6, 1])
  256. a = np.poly1d([-1j, (1 + 2j), -(2 + 1j), 1])
  257. q, r = np.polydiv(b, a)
  258. assert_equal(q.coeffs.dtype, np.complex128)
  259. assert_equal(r.coeffs.dtype, np.complex128)
  260. assert_equal(q * a + r, b)
  261. c = [1, 2, 3]
  262. d = np.poly1d([1, 2, 3])
  263. s, t = np.polydiv(c, d)
  264. assert isinstance(s, np.poly1d)
  265. assert isinstance(t, np.poly1d)
  266. u, v = np.polydiv(d, c)
  267. assert isinstance(u, np.poly1d)
  268. assert isinstance(v, np.poly1d)
  269. def test_poly_coeffs_mutable(self):
  270. """ Coefficients should be modifiable """
  271. p = np.poly1d([1, 2, 3])
  272. p.coeffs += 1
  273. assert_equal(p.coeffs, [2, 3, 4])
  274. p.coeffs[2] += 10
  275. assert_equal(p.coeffs, [2, 3, 14])
  276. # this never used to be allowed - let's not add features to deprecated
  277. # APIs
  278. assert_raises(AttributeError, setattr, p, 'coeffs', np.array(1))