_polynomial_impl.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458
  1. """
  2. Functions to operate on polynomials.
  3. """
  4. __all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd',
  5. 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d',
  6. 'polyfit']
  7. import functools
  8. import re
  9. import warnings
  10. from .._utils import set_module
  11. import numpy._core.numeric as NX
  12. from numpy._core import (isscalar, abs, finfo, atleast_1d, hstack, dot, array,
  13. ones)
  14. from numpy._core import overrides
  15. from numpy.exceptions import RankWarning
  16. from numpy.lib._twodim_base_impl import diag, vander
  17. from numpy.lib._function_base_impl import trim_zeros
  18. from numpy.lib._type_check_impl import iscomplex, real, imag, mintypecode
  19. from numpy.linalg import eigvals, lstsq, inv
  20. array_function_dispatch = functools.partial(
  21. overrides.array_function_dispatch, module='numpy')
  22. def _poly_dispatcher(seq_of_zeros):
  23. return seq_of_zeros
  24. @array_function_dispatch(_poly_dispatcher)
  25. def poly(seq_of_zeros):
  26. """
  27. Find the coefficients of a polynomial with the given sequence of roots.
  28. .. note::
  29. This forms part of the old polynomial API. Since version 1.4, the
  30. new polynomial API defined in `numpy.polynomial` is preferred.
  31. A summary of the differences can be found in the
  32. :doc:`transition guide </reference/routines.polynomials>`.
  33. Returns the coefficients of the polynomial whose leading coefficient
  34. is one for the given sequence of zeros (multiple roots must be included
  35. in the sequence as many times as their multiplicity; see Examples).
  36. A square matrix (or array, which will be treated as a matrix) can also
  37. be given, in which case the coefficients of the characteristic polynomial
  38. of the matrix are returned.
  39. Parameters
  40. ----------
  41. seq_of_zeros : array_like, shape (N,) or (N, N)
  42. A sequence of polynomial roots, or a square array or matrix object.
  43. Returns
  44. -------
  45. c : ndarray
  46. 1D array of polynomial coefficients from highest to lowest degree:
  47. ``c[0] * x**(N) + c[1] * x**(N-1) + ... + c[N-1] * x + c[N]``
  48. where c[0] always equals 1.
  49. Raises
  50. ------
  51. ValueError
  52. If input is the wrong shape (the input must be a 1-D or square
  53. 2-D array).
  54. See Also
  55. --------
  56. polyval : Compute polynomial values.
  57. roots : Return the roots of a polynomial.
  58. polyfit : Least squares polynomial fit.
  59. poly1d : A one-dimensional polynomial class.
  60. Notes
  61. -----
  62. Specifying the roots of a polynomial still leaves one degree of
  63. freedom, typically represented by an undetermined leading
  64. coefficient. [1]_ In the case of this function, that coefficient -
  65. the first one in the returned array - is always taken as one. (If
  66. for some reason you have one other point, the only automatic way
  67. presently to leverage that information is to use ``polyfit``.)
  68. The characteristic polynomial, :math:`p_a(t)`, of an `n`-by-`n`
  69. matrix **A** is given by
  70. :math:`p_a(t) = \\mathrm{det}(t\\, \\mathbf{I} - \\mathbf{A})`,
  71. where **I** is the `n`-by-`n` identity matrix. [2]_
  72. References
  73. ----------
  74. .. [1] M. Sullivan and M. Sullivan, III, "Algebra and Trigonometry,
  75. Enhanced With Graphing Utilities," Prentice-Hall, pg. 318, 1996.
  76. .. [2] G. Strang, "Linear Algebra and Its Applications, 2nd Edition,"
  77. Academic Press, pg. 182, 1980.
  78. Examples
  79. --------
  80. Given a sequence of a polynomial's zeros:
  81. >>> import numpy as np
  82. >>> np.poly((0, 0, 0)) # Multiple root example
  83. array([1., 0., 0., 0.])
  84. The line above represents z**3 + 0*z**2 + 0*z + 0.
  85. >>> np.poly((-1./2, 0, 1./2))
  86. array([ 1. , 0. , -0.25, 0. ])
  87. The line above represents z**3 - z/4
  88. >>> np.poly((np.random.random(1)[0], 0, np.random.random(1)[0]))
  89. array([ 1. , -0.77086955, 0.08618131, 0. ]) # random
  90. Given a square array object:
  91. >>> P = np.array([[0, 1./3], [-1./2, 0]])
  92. >>> np.poly(P)
  93. array([1. , 0. , 0.16666667])
  94. Note how in all cases the leading coefficient is always 1.
  95. """
  96. seq_of_zeros = atleast_1d(seq_of_zeros)
  97. sh = seq_of_zeros.shape
  98. if len(sh) == 2 and sh[0] == sh[1] and sh[0] != 0:
  99. seq_of_zeros = eigvals(seq_of_zeros)
  100. elif len(sh) == 1:
  101. dt = seq_of_zeros.dtype
  102. # Let object arrays slip through, e.g. for arbitrary precision
  103. if dt != object:
  104. seq_of_zeros = seq_of_zeros.astype(mintypecode(dt.char))
  105. else:
  106. raise ValueError("input must be 1d or non-empty square 2d array.")
  107. if len(seq_of_zeros) == 0:
  108. return 1.0
  109. dt = seq_of_zeros.dtype
  110. a = ones((1,), dtype=dt)
  111. for zero in seq_of_zeros:
  112. a = NX.convolve(a, array([1, -zero], dtype=dt), mode='full')
  113. if issubclass(a.dtype.type, NX.complexfloating):
  114. # if complex roots are all complex conjugates, the roots are real.
  115. roots = NX.asarray(seq_of_zeros, complex)
  116. if NX.all(NX.sort(roots) == NX.sort(roots.conjugate())):
  117. a = a.real.copy()
  118. return a
  119. def _roots_dispatcher(p):
  120. return p
  121. @array_function_dispatch(_roots_dispatcher)
  122. def roots(p):
  123. """
  124. Return the roots of a polynomial with coefficients given in p.
  125. .. note::
  126. This forms part of the old polynomial API. Since version 1.4, the
  127. new polynomial API defined in `numpy.polynomial` is preferred.
  128. A summary of the differences can be found in the
  129. :doc:`transition guide </reference/routines.polynomials>`.
  130. The values in the rank-1 array `p` are coefficients of a polynomial.
  131. If the length of `p` is n+1 then the polynomial is described by::
  132. p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
  133. Parameters
  134. ----------
  135. p : array_like
  136. Rank-1 array of polynomial coefficients.
  137. Returns
  138. -------
  139. out : ndarray
  140. An array containing the roots of the polynomial.
  141. Raises
  142. ------
  143. ValueError
  144. When `p` cannot be converted to a rank-1 array.
  145. See also
  146. --------
  147. poly : Find the coefficients of a polynomial with a given sequence
  148. of roots.
  149. polyval : Compute polynomial values.
  150. polyfit : Least squares polynomial fit.
  151. poly1d : A one-dimensional polynomial class.
  152. Notes
  153. -----
  154. The algorithm relies on computing the eigenvalues of the
  155. companion matrix [1]_.
  156. References
  157. ----------
  158. .. [1] R. A. Horn & C. R. Johnson, *Matrix Analysis*. Cambridge, UK:
  159. Cambridge University Press, 1999, pp. 146-7.
  160. Examples
  161. --------
  162. >>> import numpy as np
  163. >>> coeff = [3.2, 2, 1]
  164. >>> np.roots(coeff)
  165. array([-0.3125+0.46351241j, -0.3125-0.46351241j])
  166. """
  167. # If input is scalar, this makes it an array
  168. p = atleast_1d(p)
  169. if p.ndim != 1:
  170. raise ValueError("Input must be a rank-1 array.")
  171. # find non-zero array entries
  172. non_zero = NX.nonzero(NX.ravel(p))[0]
  173. # Return an empty array if polynomial is all zeros
  174. if len(non_zero) == 0:
  175. return NX.array([])
  176. # find the number of trailing zeros -- this is the number of roots at 0.
  177. trailing_zeros = len(p) - non_zero[-1] - 1
  178. # strip leading and trailing zeros
  179. p = p[int(non_zero[0]):int(non_zero[-1])+1]
  180. # casting: if incoming array isn't floating point, make it floating point.
  181. if not issubclass(p.dtype.type, (NX.floating, NX.complexfloating)):
  182. p = p.astype(float)
  183. N = len(p)
  184. if N > 1:
  185. # build companion matrix and find its eigenvalues (the roots)
  186. A = diag(NX.ones((N-2,), p.dtype), -1)
  187. A[0,:] = -p[1:] / p[0]
  188. roots = eigvals(A)
  189. else:
  190. roots = NX.array([])
  191. # tack any zeros onto the back of the array
  192. roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype)))
  193. return roots
  194. def _polyint_dispatcher(p, m=None, k=None):
  195. return (p,)
  196. @array_function_dispatch(_polyint_dispatcher)
  197. def polyint(p, m=1, k=None):
  198. """
  199. Return an antiderivative (indefinite integral) of a polynomial.
  200. .. note::
  201. This forms part of the old polynomial API. Since version 1.4, the
  202. new polynomial API defined in `numpy.polynomial` is preferred.
  203. A summary of the differences can be found in the
  204. :doc:`transition guide </reference/routines.polynomials>`.
  205. The returned order `m` antiderivative `P` of polynomial `p` satisfies
  206. :math:`\\frac{d^m}{dx^m}P(x) = p(x)` and is defined up to `m - 1`
  207. integration constants `k`. The constants determine the low-order
  208. polynomial part
  209. .. math:: \\frac{k_{m-1}}{0!} x^0 + \\ldots + \\frac{k_0}{(m-1)!}x^{m-1}
  210. of `P` so that :math:`P^{(j)}(0) = k_{m-j-1}`.
  211. Parameters
  212. ----------
  213. p : array_like or poly1d
  214. Polynomial to integrate.
  215. A sequence is interpreted as polynomial coefficients, see `poly1d`.
  216. m : int, optional
  217. Order of the antiderivative. (Default: 1)
  218. k : list of `m` scalars or scalar, optional
  219. Integration constants. They are given in the order of integration:
  220. those corresponding to highest-order terms come first.
  221. If ``None`` (default), all constants are assumed to be zero.
  222. If `m = 1`, a single scalar can be given instead of a list.
  223. See Also
  224. --------
  225. polyder : derivative of a polynomial
  226. poly1d.integ : equivalent method
  227. Examples
  228. --------
  229. The defining property of the antiderivative:
  230. >>> import numpy as np
  231. >>> p = np.poly1d([1,1,1])
  232. >>> P = np.polyint(p)
  233. >>> P
  234. poly1d([ 0.33333333, 0.5 , 1. , 0. ]) # may vary
  235. >>> np.polyder(P) == p
  236. True
  237. The integration constants default to zero, but can be specified:
  238. >>> P = np.polyint(p, 3)
  239. >>> P(0)
  240. 0.0
  241. >>> np.polyder(P)(0)
  242. 0.0
  243. >>> np.polyder(P, 2)(0)
  244. 0.0
  245. >>> P = np.polyint(p, 3, k=[6,5,3])
  246. >>> P
  247. poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ]) # may vary
  248. Note that 3 = 6 / 2!, and that the constants are given in the order of
  249. integrations. Constant of the highest-order polynomial term comes first:
  250. >>> np.polyder(P, 2)(0)
  251. 6.0
  252. >>> np.polyder(P, 1)(0)
  253. 5.0
  254. >>> P(0)
  255. 3.0
  256. """
  257. m = int(m)
  258. if m < 0:
  259. raise ValueError("Order of integral must be positive (see polyder)")
  260. if k is None:
  261. k = NX.zeros(m, float)
  262. k = atleast_1d(k)
  263. if len(k) == 1 and m > 1:
  264. k = k[0]*NX.ones(m, float)
  265. if len(k) < m:
  266. raise ValueError(
  267. "k must be a scalar or a rank-1 array of length 1 or >m.")
  268. truepoly = isinstance(p, poly1d)
  269. p = NX.asarray(p)
  270. if m == 0:
  271. if truepoly:
  272. return poly1d(p)
  273. return p
  274. else:
  275. # Note: this must work also with object and integer arrays
  276. y = NX.concatenate((p.__truediv__(NX.arange(len(p), 0, -1)), [k[0]]))
  277. val = polyint(y, m - 1, k=k[1:])
  278. if truepoly:
  279. return poly1d(val)
  280. return val
  281. def _polyder_dispatcher(p, m=None):
  282. return (p,)
  283. @array_function_dispatch(_polyder_dispatcher)
  284. def polyder(p, m=1):
  285. """
  286. Return the derivative of the specified order of a polynomial.
  287. .. note::
  288. This forms part of the old polynomial API. Since version 1.4, the
  289. new polynomial API defined in `numpy.polynomial` is preferred.
  290. A summary of the differences can be found in the
  291. :doc:`transition guide </reference/routines.polynomials>`.
  292. Parameters
  293. ----------
  294. p : poly1d or sequence
  295. Polynomial to differentiate.
  296. A sequence is interpreted as polynomial coefficients, see `poly1d`.
  297. m : int, optional
  298. Order of differentiation (default: 1)
  299. Returns
  300. -------
  301. der : poly1d
  302. A new polynomial representing the derivative.
  303. See Also
  304. --------
  305. polyint : Anti-derivative of a polynomial.
  306. poly1d : Class for one-dimensional polynomials.
  307. Examples
  308. --------
  309. The derivative of the polynomial :math:`x^3 + x^2 + x^1 + 1` is:
  310. >>> import numpy as np
  311. >>> p = np.poly1d([1,1,1,1])
  312. >>> p2 = np.polyder(p)
  313. >>> p2
  314. poly1d([3, 2, 1])
  315. which evaluates to:
  316. >>> p2(2.)
  317. 17.0
  318. We can verify this, approximating the derivative with
  319. ``(f(x + h) - f(x))/h``:
  320. >>> (p(2. + 0.001) - p(2.)) / 0.001
  321. 17.007000999997857
  322. The fourth-order derivative of a 3rd-order polynomial is zero:
  323. >>> np.polyder(p, 2)
  324. poly1d([6, 2])
  325. >>> np.polyder(p, 3)
  326. poly1d([6])
  327. >>> np.polyder(p, 4)
  328. poly1d([0])
  329. """
  330. m = int(m)
  331. if m < 0:
  332. raise ValueError("Order of derivative must be positive (see polyint)")
  333. truepoly = isinstance(p, poly1d)
  334. p = NX.asarray(p)
  335. n = len(p) - 1
  336. y = p[:-1] * NX.arange(n, 0, -1)
  337. if m == 0:
  338. val = p
  339. else:
  340. val = polyder(y, m - 1)
  341. if truepoly:
  342. val = poly1d(val)
  343. return val
  344. def _polyfit_dispatcher(x, y, deg, rcond=None, full=None, w=None, cov=None):
  345. return (x, y, w)
  346. @array_function_dispatch(_polyfit_dispatcher)
  347. def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False):
  348. """
  349. Least squares polynomial fit.
  350. .. note::
  351. This forms part of the old polynomial API. Since version 1.4, the
  352. new polynomial API defined in `numpy.polynomial` is preferred.
  353. A summary of the differences can be found in the
  354. :doc:`transition guide </reference/routines.polynomials>`.
  355. Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg`
  356. to points `(x, y)`. Returns a vector of coefficients `p` that minimises
  357. the squared error in the order `deg`, `deg-1`, ... `0`.
  358. The `Polynomial.fit <numpy.polynomial.polynomial.Polynomial.fit>` class
  359. method is recommended for new code as it is more stable numerically. See
  360. the documentation of the method for more information.
  361. Parameters
  362. ----------
  363. x : array_like, shape (M,)
  364. x-coordinates of the M sample points ``(x[i], y[i])``.
  365. y : array_like, shape (M,) or (M, K)
  366. y-coordinates of the sample points. Several data sets of sample
  367. points sharing the same x-coordinates can be fitted at once by
  368. passing in a 2D-array that contains one dataset per column.
  369. deg : int
  370. Degree of the fitting polynomial
  371. rcond : float, optional
  372. Relative condition number of the fit. Singular values smaller than
  373. this relative to the largest singular value will be ignored. The
  374. default value is len(x)*eps, where eps is the relative precision of
  375. the float type, about 2e-16 in most cases.
  376. full : bool, optional
  377. Switch determining nature of return value. When it is False (the
  378. default) just the coefficients are returned, when True diagnostic
  379. information from the singular value decomposition is also returned.
  380. w : array_like, shape (M,), optional
  381. Weights. If not None, the weight ``w[i]`` applies to the unsquared
  382. residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are
  383. chosen so that the errors of the products ``w[i]*y[i]`` all have the
  384. same variance. When using inverse-variance weighting, use
  385. ``w[i] = 1/sigma(y[i])``. The default value is None.
  386. cov : bool or str, optional
  387. If given and not `False`, return not just the estimate but also its
  388. covariance matrix. By default, the covariance are scaled by
  389. chi2/dof, where dof = M - (deg + 1), i.e., the weights are presumed
  390. to be unreliable except in a relative sense and everything is scaled
  391. such that the reduced chi2 is unity. This scaling is omitted if
  392. ``cov='unscaled'``, as is relevant for the case that the weights are
  393. w = 1/sigma, with sigma known to be a reliable estimate of the
  394. uncertainty.
  395. Returns
  396. -------
  397. p : ndarray, shape (deg + 1,) or (deg + 1, K)
  398. Polynomial coefficients, highest power first. If `y` was 2-D, the
  399. coefficients for `k`-th data set are in ``p[:,k]``.
  400. residuals, rank, singular_values, rcond
  401. These values are only returned if ``full == True``
  402. - residuals -- sum of squared residuals of the least squares fit
  403. - rank -- the effective rank of the scaled Vandermonde
  404. coefficient matrix
  405. - singular_values -- singular values of the scaled Vandermonde
  406. coefficient matrix
  407. - rcond -- value of `rcond`.
  408. For more details, see `numpy.linalg.lstsq`.
  409. V : ndarray, shape (deg + 1, deg + 1) or (deg + 1, deg + 1, K)
  410. Present only if ``full == False`` and ``cov == True``. The covariance
  411. matrix of the polynomial coefficient estimates. The diagonal of
  412. this matrix are the variance estimates for each coefficient. If y
  413. is a 2-D array, then the covariance matrix for the `k`-th data set
  414. are in ``V[:,:,k]``
  415. Warns
  416. -----
  417. RankWarning
  418. The rank of the coefficient matrix in the least-squares fit is
  419. deficient. The warning is only raised if ``full == False``.
  420. The warnings can be turned off by
  421. >>> import warnings
  422. >>> warnings.simplefilter('ignore', np.exceptions.RankWarning)
  423. See Also
  424. --------
  425. polyval : Compute polynomial values.
  426. linalg.lstsq : Computes a least-squares fit.
  427. scipy.interpolate.UnivariateSpline : Computes spline fits.
  428. Notes
  429. -----
  430. The solution minimizes the squared error
  431. .. math::
  432. E = \\sum_{j=0}^k |p(x_j) - y_j|^2
  433. in the equations::
  434. x[0]**n * p[0] + ... + x[0] * p[n-1] + p[n] = y[0]
  435. x[1]**n * p[0] + ... + x[1] * p[n-1] + p[n] = y[1]
  436. ...
  437. x[k]**n * p[0] + ... + x[k] * p[n-1] + p[n] = y[k]
  438. The coefficient matrix of the coefficients `p` is a Vandermonde matrix.
  439. `polyfit` issues a `~exceptions.RankWarning` when the least-squares fit is
  440. badly conditioned. This implies that the best fit is not well-defined due
  441. to numerical error. The results may be improved by lowering the polynomial
  442. degree or by replacing `x` by `x` - `x`.mean(). The `rcond` parameter
  443. can also be set to a value smaller than its default, but the resulting
  444. fit may be spurious: including contributions from the small singular
  445. values can add numerical noise to the result.
  446. Note that fitting polynomial coefficients is inherently badly conditioned
  447. when the degree of the polynomial is large or the interval of sample points
  448. is badly centered. The quality of the fit should always be checked in these
  449. cases. When polynomial fits are not satisfactory, splines may be a good
  450. alternative.
  451. References
  452. ----------
  453. .. [1] Wikipedia, "Curve fitting",
  454. https://en.wikipedia.org/wiki/Curve_fitting
  455. .. [2] Wikipedia, "Polynomial interpolation",
  456. https://en.wikipedia.org/wiki/Polynomial_interpolation
  457. Examples
  458. --------
  459. >>> import numpy as np
  460. >>> import warnings
  461. >>> x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
  462. >>> y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0])
  463. >>> z = np.polyfit(x, y, 3)
  464. >>> z
  465. array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254]) # may vary
  466. It is convenient to use `poly1d` objects for dealing with polynomials:
  467. >>> p = np.poly1d(z)
  468. >>> p(0.5)
  469. 0.6143849206349179 # may vary
  470. >>> p(3.5)
  471. -0.34732142857143039 # may vary
  472. >>> p(10)
  473. 22.579365079365115 # may vary
  474. High-order polynomials may oscillate wildly:
  475. >>> with warnings.catch_warnings():
  476. ... warnings.simplefilter('ignore', np.exceptions.RankWarning)
  477. ... p30 = np.poly1d(np.polyfit(x, y, 30))
  478. ...
  479. >>> p30(4)
  480. -0.80000000000000204 # may vary
  481. >>> p30(5)
  482. -0.99999999999999445 # may vary
  483. >>> p30(4.5)
  484. -0.10547061179440398 # may vary
  485. Illustration:
  486. >>> import matplotlib.pyplot as plt
  487. >>> xp = np.linspace(-2, 6, 100)
  488. >>> _ = plt.plot(x, y, '.', xp, p(xp), '-', xp, p30(xp), '--')
  489. >>> plt.ylim(-2,2)
  490. (-2, 2)
  491. >>> plt.show()
  492. """
  493. order = int(deg) + 1
  494. x = NX.asarray(x) + 0.0
  495. y = NX.asarray(y) + 0.0
  496. # check arguments.
  497. if deg < 0:
  498. raise ValueError("expected deg >= 0")
  499. if x.ndim != 1:
  500. raise TypeError("expected 1D vector for x")
  501. if x.size == 0:
  502. raise TypeError("expected non-empty vector for x")
  503. if y.ndim < 1 or y.ndim > 2:
  504. raise TypeError("expected 1D or 2D array for y")
  505. if x.shape[0] != y.shape[0]:
  506. raise TypeError("expected x and y to have same length")
  507. # set rcond
  508. if rcond is None:
  509. rcond = len(x)*finfo(x.dtype).eps
  510. # set up least squares equation for powers of x
  511. lhs = vander(x, order)
  512. rhs = y
  513. # apply weighting
  514. if w is not None:
  515. w = NX.asarray(w) + 0.0
  516. if w.ndim != 1:
  517. raise TypeError("expected a 1-d array for weights")
  518. if w.shape[0] != y.shape[0]:
  519. raise TypeError("expected w and y to have the same length")
  520. lhs *= w[:, NX.newaxis]
  521. if rhs.ndim == 2:
  522. rhs *= w[:, NX.newaxis]
  523. else:
  524. rhs *= w
  525. # scale lhs to improve condition number and solve
  526. scale = NX.sqrt((lhs*lhs).sum(axis=0))
  527. lhs /= scale
  528. c, resids, rank, s = lstsq(lhs, rhs, rcond)
  529. c = (c.T/scale).T # broadcast scale coefficients
  530. # warn on rank reduction, which indicates an ill conditioned matrix
  531. if rank != order and not full:
  532. msg = "Polyfit may be poorly conditioned"
  533. warnings.warn(msg, RankWarning, stacklevel=2)
  534. if full:
  535. return c, resids, rank, s, rcond
  536. elif cov:
  537. Vbase = inv(dot(lhs.T, lhs))
  538. Vbase /= NX.outer(scale, scale)
  539. if cov == "unscaled":
  540. fac = 1
  541. else:
  542. if len(x) <= order:
  543. raise ValueError("the number of data points must exceed order "
  544. "to scale the covariance matrix")
  545. # note, this used to be: fac = resids / (len(x) - order - 2.0)
  546. # it was decided that the "- 2" (originally justified by "Bayesian
  547. # uncertainty analysis") is not what the user expects
  548. # (see gh-11196 and gh-11197)
  549. fac = resids / (len(x) - order)
  550. if y.ndim == 1:
  551. return c, Vbase * fac
  552. else:
  553. return c, Vbase[:,:, NX.newaxis] * fac
  554. else:
  555. return c
  556. def _polyval_dispatcher(p, x):
  557. return (p, x)
  558. @array_function_dispatch(_polyval_dispatcher)
  559. def polyval(p, x):
  560. """
  561. Evaluate a polynomial at specific values.
  562. .. note::
  563. This forms part of the old polynomial API. Since version 1.4, the
  564. new polynomial API defined in `numpy.polynomial` is preferred.
  565. A summary of the differences can be found in the
  566. :doc:`transition guide </reference/routines.polynomials>`.
  567. If `p` is of length N, this function returns the value::
  568. p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]
  569. If `x` is a sequence, then ``p(x)`` is returned for each element of ``x``.
  570. If `x` is another polynomial then the composite polynomial ``p(x(t))``
  571. is returned.
  572. Parameters
  573. ----------
  574. p : array_like or poly1d object
  575. 1D array of polynomial coefficients (including coefficients equal
  576. to zero) from highest degree to the constant term, or an
  577. instance of poly1d.
  578. x : array_like or poly1d object
  579. A number, an array of numbers, or an instance of poly1d, at
  580. which to evaluate `p`.
  581. Returns
  582. -------
  583. values : ndarray or poly1d
  584. If `x` is a poly1d instance, the result is the composition of the two
  585. polynomials, i.e., `x` is "substituted" in `p` and the simplified
  586. result is returned. In addition, the type of `x` - array_like or
  587. poly1d - governs the type of the output: `x` array_like => `values`
  588. array_like, `x` a poly1d object => `values` is also.
  589. See Also
  590. --------
  591. poly1d: A polynomial class.
  592. Notes
  593. -----
  594. Horner's scheme [1]_ is used to evaluate the polynomial. Even so,
  595. for polynomials of high degree the values may be inaccurate due to
  596. rounding errors. Use carefully.
  597. If `x` is a subtype of `ndarray` the return value will be of the same type.
  598. References
  599. ----------
  600. .. [1] I. N. Bronshtein, K. A. Semendyayev, and K. A. Hirsch (Eng.
  601. trans. Ed.), *Handbook of Mathematics*, New York, Van Nostrand
  602. Reinhold Co., 1985, pg. 720.
  603. Examples
  604. --------
  605. >>> import numpy as np
  606. >>> np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1
  607. 76
  608. >>> np.polyval([3,0,1], np.poly1d(5))
  609. poly1d([76])
  610. >>> np.polyval(np.poly1d([3,0,1]), 5)
  611. 76
  612. >>> np.polyval(np.poly1d([3,0,1]), np.poly1d(5))
  613. poly1d([76])
  614. """
  615. p = NX.asarray(p)
  616. if isinstance(x, poly1d):
  617. y = 0
  618. else:
  619. x = NX.asanyarray(x)
  620. y = NX.zeros_like(x)
  621. for pv in p:
  622. y = y * x + pv
  623. return y
  624. def _binary_op_dispatcher(a1, a2):
  625. return (a1, a2)
  626. @array_function_dispatch(_binary_op_dispatcher)
  627. def polyadd(a1, a2):
  628. """
  629. Find the sum of two polynomials.
  630. .. note::
  631. This forms part of the old polynomial API. Since version 1.4, the
  632. new polynomial API defined in `numpy.polynomial` is preferred.
  633. A summary of the differences can be found in the
  634. :doc:`transition guide </reference/routines.polynomials>`.
  635. Returns the polynomial resulting from the sum of two input polynomials.
  636. Each input must be either a poly1d object or a 1D sequence of polynomial
  637. coefficients, from highest to lowest degree.
  638. Parameters
  639. ----------
  640. a1, a2 : array_like or poly1d object
  641. Input polynomials.
  642. Returns
  643. -------
  644. out : ndarray or poly1d object
  645. The sum of the inputs. If either input is a poly1d object, then the
  646. output is also a poly1d object. Otherwise, it is a 1D array of
  647. polynomial coefficients from highest to lowest degree.
  648. See Also
  649. --------
  650. poly1d : A one-dimensional polynomial class.
  651. poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
  652. Examples
  653. --------
  654. >>> import numpy as np
  655. >>> np.polyadd([1, 2], [9, 5, 4])
  656. array([9, 6, 6])
  657. Using poly1d objects:
  658. >>> p1 = np.poly1d([1, 2])
  659. >>> p2 = np.poly1d([9, 5, 4])
  660. >>> print(p1)
  661. 1 x + 2
  662. >>> print(p2)
  663. 2
  664. 9 x + 5 x + 4
  665. >>> print(np.polyadd(p1, p2))
  666. 2
  667. 9 x + 6 x + 6
  668. """
  669. truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
  670. a1 = atleast_1d(a1)
  671. a2 = atleast_1d(a2)
  672. diff = len(a2) - len(a1)
  673. if diff == 0:
  674. val = a1 + a2
  675. elif diff > 0:
  676. zr = NX.zeros(diff, a1.dtype)
  677. val = NX.concatenate((zr, a1)) + a2
  678. else:
  679. zr = NX.zeros(abs(diff), a2.dtype)
  680. val = a1 + NX.concatenate((zr, a2))
  681. if truepoly:
  682. val = poly1d(val)
  683. return val
  684. @array_function_dispatch(_binary_op_dispatcher)
  685. def polysub(a1, a2):
  686. """
  687. Difference (subtraction) of two polynomials.
  688. .. note::
  689. This forms part of the old polynomial API. Since version 1.4, the
  690. new polynomial API defined in `numpy.polynomial` is preferred.
  691. A summary of the differences can be found in the
  692. :doc:`transition guide </reference/routines.polynomials>`.
  693. Given two polynomials `a1` and `a2`, returns ``a1 - a2``.
  694. `a1` and `a2` can be either array_like sequences of the polynomials'
  695. coefficients (including coefficients equal to zero), or `poly1d` objects.
  696. Parameters
  697. ----------
  698. a1, a2 : array_like or poly1d
  699. Minuend and subtrahend polynomials, respectively.
  700. Returns
  701. -------
  702. out : ndarray or poly1d
  703. Array or `poly1d` object of the difference polynomial's coefficients.
  704. See Also
  705. --------
  706. polyval, polydiv, polymul, polyadd
  707. Examples
  708. --------
  709. .. math:: (2 x^2 + 10 x - 2) - (3 x^2 + 10 x -4) = (-x^2 + 2)
  710. >>> import numpy as np
  711. >>> np.polysub([2, 10, -2], [3, 10, -4])
  712. array([-1, 0, 2])
  713. """
  714. truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
  715. a1 = atleast_1d(a1)
  716. a2 = atleast_1d(a2)
  717. diff = len(a2) - len(a1)
  718. if diff == 0:
  719. val = a1 - a2
  720. elif diff > 0:
  721. zr = NX.zeros(diff, a1.dtype)
  722. val = NX.concatenate((zr, a1)) - a2
  723. else:
  724. zr = NX.zeros(abs(diff), a2.dtype)
  725. val = a1 - NX.concatenate((zr, a2))
  726. if truepoly:
  727. val = poly1d(val)
  728. return val
  729. @array_function_dispatch(_binary_op_dispatcher)
  730. def polymul(a1, a2):
  731. """
  732. Find the product of two polynomials.
  733. .. note::
  734. This forms part of the old polynomial API. Since version 1.4, the
  735. new polynomial API defined in `numpy.polynomial` is preferred.
  736. A summary of the differences can be found in the
  737. :doc:`transition guide </reference/routines.polynomials>`.
  738. Finds the polynomial resulting from the multiplication of the two input
  739. polynomials. Each input must be either a poly1d object or a 1D sequence
  740. of polynomial coefficients, from highest to lowest degree.
  741. Parameters
  742. ----------
  743. a1, a2 : array_like or poly1d object
  744. Input polynomials.
  745. Returns
  746. -------
  747. out : ndarray or poly1d object
  748. The polynomial resulting from the multiplication of the inputs. If
  749. either inputs is a poly1d object, then the output is also a poly1d
  750. object. Otherwise, it is a 1D array of polynomial coefficients from
  751. highest to lowest degree.
  752. See Also
  753. --------
  754. poly1d : A one-dimensional polynomial class.
  755. poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
  756. convolve : Array convolution. Same output as polymul, but has parameter
  757. for overlap mode.
  758. Examples
  759. --------
  760. >>> import numpy as np
  761. >>> np.polymul([1, 2, 3], [9, 5, 1])
  762. array([ 9, 23, 38, 17, 3])
  763. Using poly1d objects:
  764. >>> p1 = np.poly1d([1, 2, 3])
  765. >>> p2 = np.poly1d([9, 5, 1])
  766. >>> print(p1)
  767. 2
  768. 1 x + 2 x + 3
  769. >>> print(p2)
  770. 2
  771. 9 x + 5 x + 1
  772. >>> print(np.polymul(p1, p2))
  773. 4 3 2
  774. 9 x + 23 x + 38 x + 17 x + 3
  775. """
  776. truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
  777. a1, a2 = poly1d(a1), poly1d(a2)
  778. val = NX.convolve(a1, a2)
  779. if truepoly:
  780. val = poly1d(val)
  781. return val
  782. def _polydiv_dispatcher(u, v):
  783. return (u, v)
  784. @array_function_dispatch(_polydiv_dispatcher)
  785. def polydiv(u, v):
  786. """
  787. Returns the quotient and remainder of polynomial division.
  788. .. note::
  789. This forms part of the old polynomial API. Since version 1.4, the
  790. new polynomial API defined in `numpy.polynomial` is preferred.
  791. A summary of the differences can be found in the
  792. :doc:`transition guide </reference/routines.polynomials>`.
  793. The input arrays are the coefficients (including any coefficients
  794. equal to zero) of the "numerator" (dividend) and "denominator"
  795. (divisor) polynomials, respectively.
  796. Parameters
  797. ----------
  798. u : array_like or poly1d
  799. Dividend polynomial's coefficients.
  800. v : array_like or poly1d
  801. Divisor polynomial's coefficients.
  802. Returns
  803. -------
  804. q : ndarray
  805. Coefficients, including those equal to zero, of the quotient.
  806. r : ndarray
  807. Coefficients, including those equal to zero, of the remainder.
  808. See Also
  809. --------
  810. poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub
  811. polyval
  812. Notes
  813. -----
  814. Both `u` and `v` must be 0-d or 1-d (ndim = 0 or 1), but `u.ndim` need
  815. not equal `v.ndim`. In other words, all four possible combinations -
  816. ``u.ndim = v.ndim = 0``, ``u.ndim = v.ndim = 1``,
  817. ``u.ndim = 1, v.ndim = 0``, and ``u.ndim = 0, v.ndim = 1`` - work.
  818. Examples
  819. --------
  820. .. math:: \\frac{3x^2 + 5x + 2}{2x + 1} = 1.5x + 1.75, remainder 0.25
  821. >>> import numpy as np
  822. >>> x = np.array([3.0, 5.0, 2.0])
  823. >>> y = np.array([2.0, 1.0])
  824. >>> np.polydiv(x, y)
  825. (array([1.5 , 1.75]), array([0.25]))
  826. """
  827. truepoly = (isinstance(u, poly1d) or isinstance(v, poly1d))
  828. u = atleast_1d(u) + 0.0
  829. v = atleast_1d(v) + 0.0
  830. # w has the common type
  831. w = u[0] + v[0]
  832. m = len(u) - 1
  833. n = len(v) - 1
  834. scale = 1. / v[0]
  835. q = NX.zeros((max(m - n + 1, 1),), w.dtype)
  836. r = u.astype(w.dtype)
  837. for k in range(0, m-n+1):
  838. d = scale * r[k]
  839. q[k] = d
  840. r[k:k+n+1] -= d*v
  841. while NX.allclose(r[0], 0, rtol=1e-14) and (r.shape[-1] > 1):
  842. r = r[1:]
  843. if truepoly:
  844. return poly1d(q), poly1d(r)
  845. return q, r
  846. _poly_mat = re.compile(r"\*\*([0-9]*)")
  847. def _raise_power(astr, wrap=70):
  848. n = 0
  849. line1 = ''
  850. line2 = ''
  851. output = ' '
  852. while True:
  853. mat = _poly_mat.search(astr, n)
  854. if mat is None:
  855. break
  856. span = mat.span()
  857. power = mat.groups()[0]
  858. partstr = astr[n:span[0]]
  859. n = span[1]
  860. toadd2 = partstr + ' '*(len(power)-1)
  861. toadd1 = ' '*(len(partstr)-1) + power
  862. if ((len(line2) + len(toadd2) > wrap) or
  863. (len(line1) + len(toadd1) > wrap)):
  864. output += line1 + "\n" + line2 + "\n "
  865. line1 = toadd1
  866. line2 = toadd2
  867. else:
  868. line2 += partstr + ' '*(len(power)-1)
  869. line1 += ' '*(len(partstr)-1) + power
  870. output += line1 + "\n" + line2
  871. return output + astr[n:]
  872. @set_module('numpy')
  873. class poly1d:
  874. """
  875. A one-dimensional polynomial class.
  876. .. note::
  877. This forms part of the old polynomial API. Since version 1.4, the
  878. new polynomial API defined in `numpy.polynomial` is preferred.
  879. A summary of the differences can be found in the
  880. :doc:`transition guide </reference/routines.polynomials>`.
  881. A convenience class, used to encapsulate "natural" operations on
  882. polynomials so that said operations may take on their customary
  883. form in code (see Examples).
  884. Parameters
  885. ----------
  886. c_or_r : array_like
  887. The polynomial's coefficients, in decreasing powers, or if
  888. the value of the second parameter is True, the polynomial's
  889. roots (values where the polynomial evaluates to 0). For example,
  890. ``poly1d([1, 2, 3])`` returns an object that represents
  891. :math:`x^2 + 2x + 3`, whereas ``poly1d([1, 2, 3], True)`` returns
  892. one that represents :math:`(x-1)(x-2)(x-3) = x^3 - 6x^2 + 11x -6`.
  893. r : bool, optional
  894. If True, `c_or_r` specifies the polynomial's roots; the default
  895. is False.
  896. variable : str, optional
  897. Changes the variable used when printing `p` from `x` to `variable`
  898. (see Examples).
  899. Examples
  900. --------
  901. Construct the polynomial :math:`x^2 + 2x + 3`:
  902. >>> import numpy as np
  903. >>> p = np.poly1d([1, 2, 3])
  904. >>> print(np.poly1d(p))
  905. 2
  906. 1 x + 2 x + 3
  907. Evaluate the polynomial at :math:`x = 0.5`:
  908. >>> p(0.5)
  909. 4.25
  910. Find the roots:
  911. >>> p.r
  912. array([-1.+1.41421356j, -1.-1.41421356j])
  913. >>> p(p.r)
  914. array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j]) # may vary
  915. These numbers in the previous line represent (0, 0) to machine precision
  916. Show the coefficients:
  917. >>> p.c
  918. array([1, 2, 3])
  919. Display the order (the leading zero-coefficients are removed):
  920. >>> p.order
  921. 2
  922. Show the coefficient of the k-th power in the polynomial
  923. (which is equivalent to ``p.c[-(i+1)]``):
  924. >>> p[1]
  925. 2
  926. Polynomials can be added, subtracted, multiplied, and divided
  927. (returns quotient and remainder):
  928. >>> p * p
  929. poly1d([ 1, 4, 10, 12, 9])
  930. >>> (p**3 + 4) / p
  931. (poly1d([ 1., 4., 10., 12., 9.]), poly1d([4.]))
  932. ``asarray(p)`` gives the coefficient array, so polynomials can be
  933. used in all functions that accept arrays:
  934. >>> p**2 # square of polynomial
  935. poly1d([ 1, 4, 10, 12, 9])
  936. >>> np.square(p) # square of individual coefficients
  937. array([1, 4, 9])
  938. The variable used in the string representation of `p` can be modified,
  939. using the `variable` parameter:
  940. >>> p = np.poly1d([1,2,3], variable='z')
  941. >>> print(p)
  942. 2
  943. 1 z + 2 z + 3
  944. Construct a polynomial from its roots:
  945. >>> np.poly1d([1, 2], True)
  946. poly1d([ 1., -3., 2.])
  947. This is the same polynomial as obtained by:
  948. >>> np.poly1d([1, -1]) * np.poly1d([1, -2])
  949. poly1d([ 1, -3, 2])
  950. """
  951. __hash__ = None
  952. @property
  953. def coeffs(self):
  954. """ The polynomial coefficients """
  955. return self._coeffs
  956. @coeffs.setter
  957. def coeffs(self, value):
  958. # allowing this makes p.coeffs *= 2 legal
  959. if value is not self._coeffs:
  960. raise AttributeError("Cannot set attribute")
  961. @property
  962. def variable(self):
  963. """ The name of the polynomial variable """
  964. return self._variable
  965. # calculated attributes
  966. @property
  967. def order(self):
  968. """ The order or degree of the polynomial """
  969. return len(self._coeffs) - 1
  970. @property
  971. def roots(self):
  972. """ The roots of the polynomial, where self(x) == 0 """
  973. return roots(self._coeffs)
  974. # our internal _coeffs property need to be backed by __dict__['coeffs'] for
  975. # scipy to work correctly.
  976. @property
  977. def _coeffs(self):
  978. return self.__dict__['coeffs']
  979. @_coeffs.setter
  980. def _coeffs(self, coeffs):
  981. self.__dict__['coeffs'] = coeffs
  982. # alias attributes
  983. r = roots
  984. c = coef = coefficients = coeffs
  985. o = order
  986. def __init__(self, c_or_r, r=False, variable=None):
  987. if isinstance(c_or_r, poly1d):
  988. self._variable = c_or_r._variable
  989. self._coeffs = c_or_r._coeffs
  990. if set(c_or_r.__dict__) - set(self.__dict__):
  991. msg = ("In the future extra properties will not be copied "
  992. "across when constructing one poly1d from another")
  993. warnings.warn(msg, FutureWarning, stacklevel=2)
  994. self.__dict__.update(c_or_r.__dict__)
  995. if variable is not None:
  996. self._variable = variable
  997. return
  998. if r:
  999. c_or_r = poly(c_or_r)
  1000. c_or_r = atleast_1d(c_or_r)
  1001. if c_or_r.ndim > 1:
  1002. raise ValueError("Polynomial must be 1d only.")
  1003. c_or_r = trim_zeros(c_or_r, trim='f')
  1004. if len(c_or_r) == 0:
  1005. c_or_r = NX.array([0], dtype=c_or_r.dtype)
  1006. self._coeffs = c_or_r
  1007. if variable is None:
  1008. variable = 'x'
  1009. self._variable = variable
  1010. def __array__(self, t=None, copy=None):
  1011. if t:
  1012. return NX.asarray(self.coeffs, t, copy=copy)
  1013. else:
  1014. return NX.asarray(self.coeffs, copy=copy)
  1015. def __repr__(self):
  1016. vals = repr(self.coeffs)
  1017. vals = vals[6:-1]
  1018. return "poly1d(%s)" % vals
  1019. def __len__(self):
  1020. return self.order
  1021. def __str__(self):
  1022. thestr = "0"
  1023. var = self.variable
  1024. # Remove leading zeros
  1025. coeffs = self.coeffs[NX.logical_or.accumulate(self.coeffs != 0)]
  1026. N = len(coeffs)-1
  1027. def fmt_float(q):
  1028. s = '%.4g' % q
  1029. if s.endswith('.0000'):
  1030. s = s[:-5]
  1031. return s
  1032. for k, coeff in enumerate(coeffs):
  1033. if not iscomplex(coeff):
  1034. coefstr = fmt_float(real(coeff))
  1035. elif real(coeff) == 0:
  1036. coefstr = '%sj' % fmt_float(imag(coeff))
  1037. else:
  1038. coefstr = '(%s + %sj)' % (fmt_float(real(coeff)),
  1039. fmt_float(imag(coeff)))
  1040. power = (N-k)
  1041. if power == 0:
  1042. if coefstr != '0':
  1043. newstr = '%s' % (coefstr,)
  1044. else:
  1045. if k == 0:
  1046. newstr = '0'
  1047. else:
  1048. newstr = ''
  1049. elif power == 1:
  1050. if coefstr == '0':
  1051. newstr = ''
  1052. elif coefstr == 'b':
  1053. newstr = var
  1054. else:
  1055. newstr = '%s %s' % (coefstr, var)
  1056. else:
  1057. if coefstr == '0':
  1058. newstr = ''
  1059. elif coefstr == 'b':
  1060. newstr = '%s**%d' % (var, power,)
  1061. else:
  1062. newstr = '%s %s**%d' % (coefstr, var, power)
  1063. if k > 0:
  1064. if newstr != '':
  1065. if newstr.startswith('-'):
  1066. thestr = "%s - %s" % (thestr, newstr[1:])
  1067. else:
  1068. thestr = "%s + %s" % (thestr, newstr)
  1069. else:
  1070. thestr = newstr
  1071. return _raise_power(thestr)
  1072. def __call__(self, val):
  1073. return polyval(self.coeffs, val)
  1074. def __neg__(self):
  1075. return poly1d(-self.coeffs)
  1076. def __pos__(self):
  1077. return self
  1078. def __mul__(self, other):
  1079. if isscalar(other):
  1080. return poly1d(self.coeffs * other)
  1081. else:
  1082. other = poly1d(other)
  1083. return poly1d(polymul(self.coeffs, other.coeffs))
  1084. def __rmul__(self, other):
  1085. if isscalar(other):
  1086. return poly1d(other * self.coeffs)
  1087. else:
  1088. other = poly1d(other)
  1089. return poly1d(polymul(self.coeffs, other.coeffs))
  1090. def __add__(self, other):
  1091. other = poly1d(other)
  1092. return poly1d(polyadd(self.coeffs, other.coeffs))
  1093. def __radd__(self, other):
  1094. other = poly1d(other)
  1095. return poly1d(polyadd(self.coeffs, other.coeffs))
  1096. def __pow__(self, val):
  1097. if not isscalar(val) or int(val) != val or val < 0:
  1098. raise ValueError("Power to non-negative integers only.")
  1099. res = [1]
  1100. for _ in range(val):
  1101. res = polymul(self.coeffs, res)
  1102. return poly1d(res)
  1103. def __sub__(self, other):
  1104. other = poly1d(other)
  1105. return poly1d(polysub(self.coeffs, other.coeffs))
  1106. def __rsub__(self, other):
  1107. other = poly1d(other)
  1108. return poly1d(polysub(other.coeffs, self.coeffs))
  1109. def __div__(self, other):
  1110. if isscalar(other):
  1111. return poly1d(self.coeffs/other)
  1112. else:
  1113. other = poly1d(other)
  1114. return polydiv(self, other)
  1115. __truediv__ = __div__
  1116. def __rdiv__(self, other):
  1117. if isscalar(other):
  1118. return poly1d(other/self.coeffs)
  1119. else:
  1120. other = poly1d(other)
  1121. return polydiv(other, self)
  1122. __rtruediv__ = __rdiv__
  1123. def __eq__(self, other):
  1124. if not isinstance(other, poly1d):
  1125. return NotImplemented
  1126. if self.coeffs.shape != other.coeffs.shape:
  1127. return False
  1128. return (self.coeffs == other.coeffs).all()
  1129. def __ne__(self, other):
  1130. if not isinstance(other, poly1d):
  1131. return NotImplemented
  1132. return not self.__eq__(other)
  1133. def __getitem__(self, val):
  1134. ind = self.order - val
  1135. if val > self.order:
  1136. return self.coeffs.dtype.type(0)
  1137. if val < 0:
  1138. return self.coeffs.dtype.type(0)
  1139. return self.coeffs[ind]
  1140. def __setitem__(self, key, val):
  1141. ind = self.order - key
  1142. if key < 0:
  1143. raise ValueError("Does not support negative powers.")
  1144. if key > self.order:
  1145. zr = NX.zeros(key-self.order, self.coeffs.dtype)
  1146. self._coeffs = NX.concatenate((zr, self.coeffs))
  1147. ind = 0
  1148. self._coeffs[ind] = val
  1149. return
  1150. def __iter__(self):
  1151. return iter(self.coeffs)
  1152. def integ(self, m=1, k=0):
  1153. """
  1154. Return an antiderivative (indefinite integral) of this polynomial.
  1155. Refer to `polyint` for full documentation.
  1156. See Also
  1157. --------
  1158. polyint : equivalent function
  1159. """
  1160. return poly1d(polyint(self.coeffs, m=m, k=k))
  1161. def deriv(self, m=1):
  1162. """
  1163. Return a derivative of this polynomial.
  1164. Refer to `polyder` for full documentation.
  1165. See Also
  1166. --------
  1167. polyder : equivalent function
  1168. """
  1169. return poly1d(polyder(self.coeffs, m=m))
  1170. # Stuff to do on module import
  1171. warnings.simplefilter('always', RankWarning)