Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Functions to operate on polynomials. 

3 

4""" 

5__all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd', 

6 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d', 

7 'polyfit', 'RankWarning'] 

8 

9import functools 

10import re 

11import warnings 

12import numpy.core.numeric as NX 

13 

14from numpy.core import (isscalar, abs, finfo, atleast_1d, hstack, dot, array, 

15 ones) 

16from numpy.core import overrides 

17from numpy.core.overrides import set_module 

18from numpy.lib.twodim_base import diag, vander 

19from numpy.lib.function_base import trim_zeros 

20from numpy.lib.type_check import iscomplex, real, imag, mintypecode 

21from numpy.linalg import eigvals, lstsq, inv 

22 

23 

24array_function_dispatch = functools.partial( 

25 overrides.array_function_dispatch, module='numpy') 

26 

27 

28@set_module('numpy') 

29class RankWarning(UserWarning): 

30 """ 

31 Issued by `polyfit` when the Vandermonde matrix is rank deficient. 

32 

33 For more information, a way to suppress the warning, and an example of 

34 `RankWarning` being issued, see `polyfit`. 

35 

36 """ 

37 pass 

38 

39 

40def _poly_dispatcher(seq_of_zeros): 

41 return seq_of_zeros 

42 

43 

44@array_function_dispatch(_poly_dispatcher) 

45def poly(seq_of_zeros): 

46 """ 

47 Find the coefficients of a polynomial with the given sequence of roots. 

48 

49 Returns the coefficients of the polynomial whose leading coefficient 

50 is one for the given sequence of zeros (multiple roots must be included 

51 in the sequence as many times as their multiplicity; see Examples). 

52 A square matrix (or array, which will be treated as a matrix) can also 

53 be given, in which case the coefficients of the characteristic polynomial 

54 of the matrix are returned. 

55 

56 Parameters 

57 ---------- 

58 seq_of_zeros : array_like, shape (N,) or (N, N) 

59 A sequence of polynomial roots, or a square array or matrix object. 

60 

61 Returns 

62 ------- 

63 c : ndarray 

64 1D array of polynomial coefficients from highest to lowest degree: 

65 

66 ``c[0] * x**(N) + c[1] * x**(N-1) + ... + c[N-1] * x + c[N]`` 

67 where c[0] always equals 1. 

68 

69 Raises 

70 ------ 

71 ValueError 

72 If input is the wrong shape (the input must be a 1-D or square 

73 2-D array). 

74 

75 See Also 

76 -------- 

77 polyval : Compute polynomial values. 

78 roots : Return the roots of a polynomial. 

79 polyfit : Least squares polynomial fit. 

80 poly1d : A one-dimensional polynomial class. 

81 

82 Notes 

83 ----- 

84 Specifying the roots of a polynomial still leaves one degree of 

85 freedom, typically represented by an undetermined leading 

86 coefficient. [1]_ In the case of this function, that coefficient - 

87 the first one in the returned array - is always taken as one. (If 

88 for some reason you have one other point, the only automatic way 

89 presently to leverage that information is to use ``polyfit``.) 

90 

91 The characteristic polynomial, :math:`p_a(t)`, of an `n`-by-`n` 

92 matrix **A** is given by 

93 

94 :math:`p_a(t) = \\mathrm{det}(t\\, \\mathbf{I} - \\mathbf{A})`, 

95 

96 where **I** is the `n`-by-`n` identity matrix. [2]_ 

97 

98 References 

99 ---------- 

100 .. [1] M. Sullivan and M. Sullivan, III, "Algebra and Trignometry, 

101 Enhanced With Graphing Utilities," Prentice-Hall, pg. 318, 1996. 

102 

103 .. [2] G. Strang, "Linear Algebra and Its Applications, 2nd Edition," 

104 Academic Press, pg. 182, 1980. 

105 

106 Examples 

107 -------- 

108 Given a sequence of a polynomial's zeros: 

109 

110 >>> np.poly((0, 0, 0)) # Multiple root example 

111 array([1., 0., 0., 0.]) 

112 

113 The line above represents z**3 + 0*z**2 + 0*z + 0. 

114 

115 >>> np.poly((-1./2, 0, 1./2)) 

116 array([ 1. , 0. , -0.25, 0. ]) 

117 

118 The line above represents z**3 - z/4 

119 

120 >>> np.poly((np.random.random(1)[0], 0, np.random.random(1)[0])) 

121 array([ 1. , -0.77086955, 0.08618131, 0. ]) # random 

122 

123 Given a square array object: 

124 

125 >>> P = np.array([[0, 1./3], [-1./2, 0]]) 

126 >>> np.poly(P) 

127 array([1. , 0. , 0.16666667]) 

128 

129 Note how in all cases the leading coefficient is always 1. 

130 

131 """ 

132 seq_of_zeros = atleast_1d(seq_of_zeros) 

133 sh = seq_of_zeros.shape 

134 

135 if len(sh) == 2 and sh[0] == sh[1] and sh[0] != 0: 

136 seq_of_zeros = eigvals(seq_of_zeros) 

137 elif len(sh) == 1: 

138 dt = seq_of_zeros.dtype 

139 # Let object arrays slip through, e.g. for arbitrary precision 

140 if dt != object: 

141 seq_of_zeros = seq_of_zeros.astype(mintypecode(dt.char)) 

142 else: 

143 raise ValueError("input must be 1d or non-empty square 2d array.") 

144 

145 if len(seq_of_zeros) == 0: 

146 return 1.0 

147 dt = seq_of_zeros.dtype 

148 a = ones((1,), dtype=dt) 

149 for k in range(len(seq_of_zeros)): 

150 a = NX.convolve(a, array([1, -seq_of_zeros[k]], dtype=dt), 

151 mode='full') 

152 

153 if issubclass(a.dtype.type, NX.complexfloating): 

154 # if complex roots are all complex conjugates, the roots are real. 

155 roots = NX.asarray(seq_of_zeros, complex) 

156 if NX.all(NX.sort(roots) == NX.sort(roots.conjugate())): 

157 a = a.real.copy() 

158 

159 return a 

160 

161 

162def _roots_dispatcher(p): 

163 return p 

164 

165 

166@array_function_dispatch(_roots_dispatcher) 

167def roots(p): 

168 """ 

169 Return the roots of a polynomial with coefficients given in p. 

170 

171 The values in the rank-1 array `p` are coefficients of a polynomial. 

172 If the length of `p` is n+1 then the polynomial is described by:: 

173 

174 p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n] 

175 

176 Parameters 

177 ---------- 

178 p : array_like 

179 Rank-1 array of polynomial coefficients. 

180 

181 Returns 

182 ------- 

183 out : ndarray 

184 An array containing the roots of the polynomial. 

185 

186 Raises 

187 ------ 

188 ValueError 

189 When `p` cannot be converted to a rank-1 array. 

190 

191 See also 

192 -------- 

193 poly : Find the coefficients of a polynomial with a given sequence 

194 of roots. 

195 polyval : Compute polynomial values. 

196 polyfit : Least squares polynomial fit. 

197 poly1d : A one-dimensional polynomial class. 

198 

199 Notes 

200 ----- 

201 The algorithm relies on computing the eigenvalues of the 

202 companion matrix [1]_. 

203 

204 References 

205 ---------- 

206 .. [1] R. A. Horn & C. R. Johnson, *Matrix Analysis*. Cambridge, UK: 

207 Cambridge University Press, 1999, pp. 146-7. 

208 

209 Examples 

210 -------- 

211 >>> coeff = [3.2, 2, 1] 

212 >>> np.roots(coeff) 

213 array([-0.3125+0.46351241j, -0.3125-0.46351241j]) 

214 

215 """ 

216 # If input is scalar, this makes it an array 

217 p = atleast_1d(p) 

218 if p.ndim != 1: 

219 raise ValueError("Input must be a rank-1 array.") 

220 

221 # find non-zero array entries 

222 non_zero = NX.nonzero(NX.ravel(p))[0] 

223 

224 # Return an empty array if polynomial is all zeros 

225 if len(non_zero) == 0: 

226 return NX.array([]) 

227 

228 # find the number of trailing zeros -- this is the number of roots at 0. 

229 trailing_zeros = len(p) - non_zero[-1] - 1 

230 

231 # strip leading and trailing zeros 

232 p = p[int(non_zero[0]):int(non_zero[-1])+1] 

233 

234 # casting: if incoming array isn't floating point, make it floating point. 

235 if not issubclass(p.dtype.type, (NX.floating, NX.complexfloating)): 

236 p = p.astype(float) 

237 

238 N = len(p) 

239 if N > 1: 

240 # build companion matrix and find its eigenvalues (the roots) 

241 A = diag(NX.ones((N-2,), p.dtype), -1) 

242 A[0,:] = -p[1:] / p[0] 

243 roots = eigvals(A) 

244 else: 

245 roots = NX.array([]) 

246 

247 # tack any zeros onto the back of the array 

248 roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype))) 

249 return roots 

250 

251 

252def _polyint_dispatcher(p, m=None, k=None): 

253 return (p,) 

254 

255 

256@array_function_dispatch(_polyint_dispatcher) 

257def polyint(p, m=1, k=None): 

258 """ 

259 Return an antiderivative (indefinite integral) of a polynomial. 

260 

261 The returned order `m` antiderivative `P` of polynomial `p` satisfies 

262 :math:`\\frac{d^m}{dx^m}P(x) = p(x)` and is defined up to `m - 1` 

263 integration constants `k`. The constants determine the low-order 

264 polynomial part 

265 

266 .. math:: \\frac{k_{m-1}}{0!} x^0 + \\ldots + \\frac{k_0}{(m-1)!}x^{m-1} 

267 

268 of `P` so that :math:`P^{(j)}(0) = k_{m-j-1}`. 

269 

270 Parameters 

271 ---------- 

272 p : array_like or poly1d 

273 Polynomial to integrate. 

274 A sequence is interpreted as polynomial coefficients, see `poly1d`. 

275 m : int, optional 

276 Order of the antiderivative. (Default: 1) 

277 k : list of `m` scalars or scalar, optional 

278 Integration constants. They are given in the order of integration: 

279 those corresponding to highest-order terms come first. 

280 

281 If ``None`` (default), all constants are assumed to be zero. 

282 If `m = 1`, a single scalar can be given instead of a list. 

283 

284 See Also 

285 -------- 

286 polyder : derivative of a polynomial 

287 poly1d.integ : equivalent method 

288 

289 Examples 

290 -------- 

291 The defining property of the antiderivative: 

292 

293 >>> p = np.poly1d([1,1,1]) 

294 >>> P = np.polyint(p) 

295 >>> P 

296 poly1d([ 0.33333333, 0.5 , 1. , 0. ]) # may vary 

297 >>> np.polyder(P) == p 

298 True 

299 

300 The integration constants default to zero, but can be specified: 

301 

302 >>> P = np.polyint(p, 3) 

303 >>> P(0) 

304 0.0 

305 >>> np.polyder(P)(0) 

306 0.0 

307 >>> np.polyder(P, 2)(0) 

308 0.0 

309 >>> P = np.polyint(p, 3, k=[6,5,3]) 

310 >>> P 

311 poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ]) # may vary 

312 

313 Note that 3 = 6 / 2!, and that the constants are given in the order of 

314 integrations. Constant of the highest-order polynomial term comes first: 

315 

316 >>> np.polyder(P, 2)(0) 

317 6.0 

318 >>> np.polyder(P, 1)(0) 

319 5.0 

320 >>> P(0) 

321 3.0 

322 

323 """ 

324 m = int(m) 

325 if m < 0: 

326 raise ValueError("Order of integral must be positive (see polyder)") 

327 if k is None: 

328 k = NX.zeros(m, float) 

329 k = atleast_1d(k) 

330 if len(k) == 1 and m > 1: 

331 k = k[0]*NX.ones(m, float) 

332 if len(k) < m: 

333 raise ValueError( 

334 "k must be a scalar or a rank-1 array of length 1 or >m.") 

335 

336 truepoly = isinstance(p, poly1d) 

337 p = NX.asarray(p) 

338 if m == 0: 

339 if truepoly: 

340 return poly1d(p) 

341 return p 

342 else: 

343 # Note: this must work also with object and integer arrays 

344 y = NX.concatenate((p.__truediv__(NX.arange(len(p), 0, -1)), [k[0]])) 

345 val = polyint(y, m - 1, k=k[1:]) 

346 if truepoly: 

347 return poly1d(val) 

348 return val 

349 

350 

351def _polyder_dispatcher(p, m=None): 

352 return (p,) 

353 

354 

355@array_function_dispatch(_polyder_dispatcher) 

356def polyder(p, m=1): 

357 """ 

358 Return the derivative of the specified order of a polynomial. 

359 

360 Parameters 

361 ---------- 

362 p : poly1d or sequence 

363 Polynomial to differentiate. 

364 A sequence is interpreted as polynomial coefficients, see `poly1d`. 

365 m : int, optional 

366 Order of differentiation (default: 1) 

367 

368 Returns 

369 ------- 

370 der : poly1d 

371 A new polynomial representing the derivative. 

372 

373 See Also 

374 -------- 

375 polyint : Anti-derivative of a polynomial. 

376 poly1d : Class for one-dimensional polynomials. 

377 

378 Examples 

379 -------- 

380 The derivative of the polynomial :math:`x^3 + x^2 + x^1 + 1` is: 

381 

382 >>> p = np.poly1d([1,1,1,1]) 

383 >>> p2 = np.polyder(p) 

384 >>> p2 

385 poly1d([3, 2, 1]) 

386 

387 which evaluates to: 

388 

389 >>> p2(2.) 

390 17.0 

391 

392 We can verify this, approximating the derivative with 

393 ``(f(x + h) - f(x))/h``: 

394 

395 >>> (p(2. + 0.001) - p(2.)) / 0.001 

396 17.007000999997857 

397 

398 The fourth-order derivative of a 3rd-order polynomial is zero: 

399 

400 >>> np.polyder(p, 2) 

401 poly1d([6, 2]) 

402 >>> np.polyder(p, 3) 

403 poly1d([6]) 

404 >>> np.polyder(p, 4) 

405 poly1d([0.]) 

406 

407 """ 

408 m = int(m) 

409 if m < 0: 

410 raise ValueError("Order of derivative must be positive (see polyint)") 

411 

412 truepoly = isinstance(p, poly1d) 

413 p = NX.asarray(p) 

414 n = len(p) - 1 

415 y = p[:-1] * NX.arange(n, 0, -1) 

416 if m == 0: 

417 val = p 

418 else: 

419 val = polyder(y, m - 1) 

420 if truepoly: 

421 val = poly1d(val) 

422 return val 

423 

424 

425def _polyfit_dispatcher(x, y, deg, rcond=None, full=None, w=None, cov=None): 

426 return (x, y, w) 

427 

428 

429@array_function_dispatch(_polyfit_dispatcher) 

430def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): 

431 """ 

432 Least squares polynomial fit. 

433 

434 Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg` 

435 to points `(x, y)`. Returns a vector of coefficients `p` that minimises 

436 the squared error in the order `deg`, `deg-1`, ... `0`. 

437 

438 The `Polynomial.fit <numpy.polynomial.polynomial.Polynomial.fit>` class 

439 method is recommended for new code as it is more stable numerically. See 

440 the documentation of the method for more information. 

441 

442 Parameters 

443 ---------- 

444 x : array_like, shape (M,) 

445 x-coordinates of the M sample points ``(x[i], y[i])``. 

446 y : array_like, shape (M,) or (M, K) 

447 y-coordinates of the sample points. Several data sets of sample 

448 points sharing the same x-coordinates can be fitted at once by 

449 passing in a 2D-array that contains one dataset per column. 

450 deg : int 

451 Degree of the fitting polynomial 

452 rcond : float, optional 

453 Relative condition number of the fit. Singular values smaller than 

454 this relative to the largest singular value will be ignored. The 

455 default value is len(x)*eps, where eps is the relative precision of 

456 the float type, about 2e-16 in most cases. 

457 full : bool, optional 

458 Switch determining nature of return value. When it is False (the 

459 default) just the coefficients are returned, when True diagnostic 

460 information from the singular value decomposition is also returned. 

461 w : array_like, shape (M,), optional 

462 Weights to apply to the y-coordinates of the sample points. For 

463 gaussian uncertainties, use 1/sigma (not 1/sigma**2). 

464 cov : bool or str, optional 

465 If given and not `False`, return not just the estimate but also its 

466 covariance matrix. By default, the covariance are scaled by 

467 chi2/sqrt(N-dof), i.e., the weights are presumed to be unreliable 

468 except in a relative sense and everything is scaled such that the 

469 reduced chi2 is unity. This scaling is omitted if ``cov='unscaled'``, 

470 as is relevant for the case that the weights are 1/sigma**2, with 

471 sigma known to be a reliable estimate of the uncertainty. 

472 

473 Returns 

474 ------- 

475 p : ndarray, shape (deg + 1,) or (deg + 1, K) 

476 Polynomial coefficients, highest power first. If `y` was 2-D, the 

477 coefficients for `k`-th data set are in ``p[:,k]``. 

478 

479 residuals, rank, singular_values, rcond 

480 Present only if `full` = True. Residuals is sum of squared residuals 

481 of the least-squares fit, the effective rank of the scaled Vandermonde 

482 coefficient matrix, its singular values, and the specified value of 

483 `rcond`. For more details, see `linalg.lstsq`. 

484 

485 V : ndarray, shape (M,M) or (M,M,K) 

486 Present only if `full` = False and `cov`=True. The covariance 

487 matrix of the polynomial coefficient estimates. The diagonal of 

488 this matrix are the variance estimates for each coefficient. If y 

489 is a 2-D array, then the covariance matrix for the `k`-th data set 

490 are in ``V[:,:,k]`` 

491 

492 

493 Warns 

494 ----- 

495 RankWarning 

496 The rank of the coefficient matrix in the least-squares fit is 

497 deficient. The warning is only raised if `full` = False. 

498 

499 The warnings can be turned off by 

500 

501 >>> import warnings 

502 >>> warnings.simplefilter('ignore', np.RankWarning) 

503 

504 See Also 

505 -------- 

506 polyval : Compute polynomial values. 

507 linalg.lstsq : Computes a least-squares fit. 

508 scipy.interpolate.UnivariateSpline : Computes spline fits. 

509 

510 Notes 

511 ----- 

512 The solution minimizes the squared error 

513 

514 .. math :: 

515 E = \\sum_{j=0}^k |p(x_j) - y_j|^2 

516 

517 in the equations:: 

518 

519 x[0]**n * p[0] + ... + x[0] * p[n-1] + p[n] = y[0] 

520 x[1]**n * p[0] + ... + x[1] * p[n-1] + p[n] = y[1] 

521 ... 

522 x[k]**n * p[0] + ... + x[k] * p[n-1] + p[n] = y[k] 

523 

524 The coefficient matrix of the coefficients `p` is a Vandermonde matrix. 

525 

526 `polyfit` issues a `RankWarning` when the least-squares fit is badly 

527 conditioned. This implies that the best fit is not well-defined due 

528 to numerical error. The results may be improved by lowering the polynomial 

529 degree or by replacing `x` by `x` - `x`.mean(). The `rcond` parameter 

530 can also be set to a value smaller than its default, but the resulting 

531 fit may be spurious: including contributions from the small singular 

532 values can add numerical noise to the result. 

533 

534 Note that fitting polynomial coefficients is inherently badly conditioned 

535 when the degree of the polynomial is large or the interval of sample points 

536 is badly centered. The quality of the fit should always be checked in these 

537 cases. When polynomial fits are not satisfactory, splines may be a good 

538 alternative. 

539 

540 References 

541 ---------- 

542 .. [1] Wikipedia, "Curve fitting", 

543 https://en.wikipedia.org/wiki/Curve_fitting 

544 .. [2] Wikipedia, "Polynomial interpolation", 

545 https://en.wikipedia.org/wiki/Polynomial_interpolation 

546 

547 Examples 

548 -------- 

549 >>> import warnings 

550 >>> x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]) 

551 >>> y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0]) 

552 >>> z = np.polyfit(x, y, 3) 

553 >>> z 

554 array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254]) # may vary 

555 

556 It is convenient to use `poly1d` objects for dealing with polynomials: 

557 

558 >>> p = np.poly1d(z) 

559 >>> p(0.5) 

560 0.6143849206349179 # may vary 

561 >>> p(3.5) 

562 -0.34732142857143039 # may vary 

563 >>> p(10) 

564 22.579365079365115 # may vary 

565 

566 High-order polynomials may oscillate wildly: 

567 

568 >>> with warnings.catch_warnings(): 

569 ... warnings.simplefilter('ignore', np.RankWarning) 

570 ... p30 = np.poly1d(np.polyfit(x, y, 30)) 

571 ... 

572 >>> p30(4) 

573 -0.80000000000000204 # may vary 

574 >>> p30(5) 

575 -0.99999999999999445 # may vary 

576 >>> p30(4.5) 

577 -0.10547061179440398 # may vary 

578 

579 Illustration: 

580 

581 >>> import matplotlib.pyplot as plt 

582 >>> xp = np.linspace(-2, 6, 100) 

583 >>> _ = plt.plot(x, y, '.', xp, p(xp), '-', xp, p30(xp), '--') 

584 >>> plt.ylim(-2,2) 

585 (-2, 2) 

586 >>> plt.show() 

587 

588 """ 

589 order = int(deg) + 1 

590 x = NX.asarray(x) + 0.0 

591 y = NX.asarray(y) + 0.0 

592 

593 # check arguments. 

594 if deg < 0: 

595 raise ValueError("expected deg >= 0") 

596 if x.ndim != 1: 

597 raise TypeError("expected 1D vector for x") 

598 if x.size == 0: 

599 raise TypeError("expected non-empty vector for x") 

600 if y.ndim < 1 or y.ndim > 2: 

601 raise TypeError("expected 1D or 2D array for y") 

602 if x.shape[0] != y.shape[0]: 

603 raise TypeError("expected x and y to have same length") 

604 

605 # set rcond 

606 if rcond is None: 

607 rcond = len(x)*finfo(x.dtype).eps 

608 

609 # set up least squares equation for powers of x 

610 lhs = vander(x, order) 

611 rhs = y 

612 

613 # apply weighting 

614 if w is not None: 

615 w = NX.asarray(w) + 0.0 

616 if w.ndim != 1: 

617 raise TypeError("expected a 1-d array for weights") 

618 if w.shape[0] != y.shape[0]: 

619 raise TypeError("expected w and y to have the same length") 

620 lhs *= w[:, NX.newaxis] 

621 if rhs.ndim == 2: 

622 rhs *= w[:, NX.newaxis] 

623 else: 

624 rhs *= w 

625 

626 # scale lhs to improve condition number and solve 

627 scale = NX.sqrt((lhs*lhs).sum(axis=0)) 

628 lhs /= scale 

629 c, resids, rank, s = lstsq(lhs, rhs, rcond) 

630 c = (c.T/scale).T # broadcast scale coefficients 

631 

632 # warn on rank reduction, which indicates an ill conditioned matrix 

633 if rank != order and not full: 

634 msg = "Polyfit may be poorly conditioned" 

635 warnings.warn(msg, RankWarning, stacklevel=4) 

636 

637 if full: 

638 return c, resids, rank, s, rcond 

639 elif cov: 

640 Vbase = inv(dot(lhs.T, lhs)) 

641 Vbase /= NX.outer(scale, scale) 

642 if cov == "unscaled": 

643 fac = 1 

644 else: 

645 if len(x) <= order: 

646 raise ValueError("the number of data points must exceed order " 

647 "to scale the covariance matrix") 

648 # note, this used to be: fac = resids / (len(x) - order - 2.0) 

649 # it was deciced that the "- 2" (originally justified by "Bayesian 

650 # uncertainty analysis") is not was the user expects 

651 # (see gh-11196 and gh-11197) 

652 fac = resids / (len(x) - order) 

653 if y.ndim == 1: 

654 return c, Vbase * fac 

655 else: 

656 return c, Vbase[:,:, NX.newaxis] * fac 

657 else: 

658 return c 

659 

660 

661def _polyval_dispatcher(p, x): 

662 return (p, x) 

663 

664 

665@array_function_dispatch(_polyval_dispatcher) 

666def polyval(p, x): 

667 """ 

668 Evaluate a polynomial at specific values. 

669 

670 If `p` is of length N, this function returns the value: 

671 

672 ``p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]`` 

673 

674 If `x` is a sequence, then `p(x)` is returned for each element of `x`. 

675 If `x` is another polynomial then the composite polynomial `p(x(t))` 

676 is returned. 

677 

678 Parameters 

679 ---------- 

680 p : array_like or poly1d object 

681 1D array of polynomial coefficients (including coefficients equal 

682 to zero) from highest degree to the constant term, or an 

683 instance of poly1d. 

684 x : array_like or poly1d object 

685 A number, an array of numbers, or an instance of poly1d, at 

686 which to evaluate `p`. 

687 

688 Returns 

689 ------- 

690 values : ndarray or poly1d 

691 If `x` is a poly1d instance, the result is the composition of the two 

692 polynomials, i.e., `x` is "substituted" in `p` and the simplified 

693 result is returned. In addition, the type of `x` - array_like or 

694 poly1d - governs the type of the output: `x` array_like => `values` 

695 array_like, `x` a poly1d object => `values` is also. 

696 

697 See Also 

698 -------- 

699 poly1d: A polynomial class. 

700 

701 Notes 

702 ----- 

703 Horner's scheme [1]_ is used to evaluate the polynomial. Even so, 

704 for polynomials of high degree the values may be inaccurate due to 

705 rounding errors. Use carefully. 

706 

707 If `x` is a subtype of `ndarray` the return value will be of the same type. 

708 

709 References 

710 ---------- 

711 .. [1] I. N. Bronshtein, K. A. Semendyayev, and K. A. Hirsch (Eng. 

712 trans. Ed.), *Handbook of Mathematics*, New York, Van Nostrand 

713 Reinhold Co., 1985, pg. 720. 

714 

715 Examples 

716 -------- 

717 >>> np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1 

718 76 

719 >>> np.polyval([3,0,1], np.poly1d(5)) 

720 poly1d([76.]) 

721 >>> np.polyval(np.poly1d([3,0,1]), 5) 

722 76 

723 >>> np.polyval(np.poly1d([3,0,1]), np.poly1d(5)) 

724 poly1d([76.]) 

725 

726 """ 

727 p = NX.asarray(p) 

728 if isinstance(x, poly1d): 

729 y = 0 

730 else: 

731 x = NX.asanyarray(x) 

732 y = NX.zeros_like(x) 

733 for i in range(len(p)): 

734 y = y * x + p[i] 

735 return y 

736 

737 

738def _binary_op_dispatcher(a1, a2): 

739 return (a1, a2) 

740 

741 

742@array_function_dispatch(_binary_op_dispatcher) 

743def polyadd(a1, a2): 

744 """ 

745 Find the sum of two polynomials. 

746 

747 Returns the polynomial resulting from the sum of two input polynomials. 

748 Each input must be either a poly1d object or a 1D sequence of polynomial 

749 coefficients, from highest to lowest degree. 

750 

751 Parameters 

752 ---------- 

753 a1, a2 : array_like or poly1d object 

754 Input polynomials. 

755 

756 Returns 

757 ------- 

758 out : ndarray or poly1d object 

759 The sum of the inputs. If either input is a poly1d object, then the 

760 output is also a poly1d object. Otherwise, it is a 1D array of 

761 polynomial coefficients from highest to lowest degree. 

762 

763 See Also 

764 -------- 

765 poly1d : A one-dimensional polynomial class. 

766 poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval 

767 

768 Examples 

769 -------- 

770 >>> np.polyadd([1, 2], [9, 5, 4]) 

771 array([9, 6, 6]) 

772 

773 Using poly1d objects: 

774 

775 >>> p1 = np.poly1d([1, 2]) 

776 >>> p2 = np.poly1d([9, 5, 4]) 

777 >>> print(p1) 

778 1 x + 2 

779 >>> print(p2) 

780 2 

781 9 x + 5 x + 4 

782 >>> print(np.polyadd(p1, p2)) 

783 2 

784 9 x + 6 x + 6 

785 

786 """ 

787 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d)) 

788 a1 = atleast_1d(a1) 

789 a2 = atleast_1d(a2) 

790 diff = len(a2) - len(a1) 

791 if diff == 0: 

792 val = a1 + a2 

793 elif diff > 0: 

794 zr = NX.zeros(diff, a1.dtype) 

795 val = NX.concatenate((zr, a1)) + a2 

796 else: 

797 zr = NX.zeros(abs(diff), a2.dtype) 

798 val = a1 + NX.concatenate((zr, a2)) 

799 if truepoly: 

800 val = poly1d(val) 

801 return val 

802 

803 

804@array_function_dispatch(_binary_op_dispatcher) 

805def polysub(a1, a2): 

806 """ 

807 Difference (subtraction) of two polynomials. 

808 

809 Given two polynomials `a1` and `a2`, returns ``a1 - a2``. 

810 `a1` and `a2` can be either array_like sequences of the polynomials' 

811 coefficients (including coefficients equal to zero), or `poly1d` objects. 

812 

813 Parameters 

814 ---------- 

815 a1, a2 : array_like or poly1d 

816 Minuend and subtrahend polynomials, respectively. 

817 

818 Returns 

819 ------- 

820 out : ndarray or poly1d 

821 Array or `poly1d` object of the difference polynomial's coefficients. 

822 

823 See Also 

824 -------- 

825 polyval, polydiv, polymul, polyadd 

826 

827 Examples 

828 -------- 

829 .. math:: (2 x^2 + 10 x - 2) - (3 x^2 + 10 x -4) = (-x^2 + 2) 

830 

831 >>> np.polysub([2, 10, -2], [3, 10, -4]) 

832 array([-1, 0, 2]) 

833 

834 """ 

835 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d)) 

836 a1 = atleast_1d(a1) 

837 a2 = atleast_1d(a2) 

838 diff = len(a2) - len(a1) 

839 if diff == 0: 

840 val = a1 - a2 

841 elif diff > 0: 

842 zr = NX.zeros(diff, a1.dtype) 

843 val = NX.concatenate((zr, a1)) - a2 

844 else: 

845 zr = NX.zeros(abs(diff), a2.dtype) 

846 val = a1 - NX.concatenate((zr, a2)) 

847 if truepoly: 

848 val = poly1d(val) 

849 return val 

850 

851 

852@array_function_dispatch(_binary_op_dispatcher) 

853def polymul(a1, a2): 

854 """ 

855 Find the product of two polynomials. 

856 

857 Finds the polynomial resulting from the multiplication of the two input 

858 polynomials. Each input must be either a poly1d object or a 1D sequence 

859 of polynomial coefficients, from highest to lowest degree. 

860 

861 Parameters 

862 ---------- 

863 a1, a2 : array_like or poly1d object 

864 Input polynomials. 

865 

866 Returns 

867 ------- 

868 out : ndarray or poly1d object 

869 The polynomial resulting from the multiplication of the inputs. If 

870 either inputs is a poly1d object, then the output is also a poly1d 

871 object. Otherwise, it is a 1D array of polynomial coefficients from 

872 highest to lowest degree. 

873 

874 See Also 

875 -------- 

876 poly1d : A one-dimensional polynomial class. 

877 poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval 

878 convolve : Array convolution. Same output as polymul, but has parameter 

879 for overlap mode. 

880 

881 Examples 

882 -------- 

883 >>> np.polymul([1, 2, 3], [9, 5, 1]) 

884 array([ 9, 23, 38, 17, 3]) 

885 

886 Using poly1d objects: 

887 

888 >>> p1 = np.poly1d([1, 2, 3]) 

889 >>> p2 = np.poly1d([9, 5, 1]) 

890 >>> print(p1) 

891 2 

892 1 x + 2 x + 3 

893 >>> print(p2) 

894 2 

895 9 x + 5 x + 1 

896 >>> print(np.polymul(p1, p2)) 

897 4 3 2 

898 9 x + 23 x + 38 x + 17 x + 3 

899 

900 """ 

901 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d)) 

902 a1, a2 = poly1d(a1), poly1d(a2) 

903 val = NX.convolve(a1, a2) 

904 if truepoly: 

905 val = poly1d(val) 

906 return val 

907 

908 

909def _polydiv_dispatcher(u, v): 

910 return (u, v) 

911 

912 

913@array_function_dispatch(_polydiv_dispatcher) 

914def polydiv(u, v): 

915 """ 

916 Returns the quotient and remainder of polynomial division. 

917 

918 The input arrays are the coefficients (including any coefficients 

919 equal to zero) of the "numerator" (dividend) and "denominator" 

920 (divisor) polynomials, respectively. 

921 

922 Parameters 

923 ---------- 

924 u : array_like or poly1d 

925 Dividend polynomial's coefficients. 

926 

927 v : array_like or poly1d 

928 Divisor polynomial's coefficients. 

929 

930 Returns 

931 ------- 

932 q : ndarray 

933 Coefficients, including those equal to zero, of the quotient. 

934 r : ndarray 

935 Coefficients, including those equal to zero, of the remainder. 

936 

937 See Also 

938 -------- 

939 poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub 

940 polyval 

941 

942 Notes 

943 ----- 

944 Both `u` and `v` must be 0-d or 1-d (ndim = 0 or 1), but `u.ndim` need 

945 not equal `v.ndim`. In other words, all four possible combinations - 

946 ``u.ndim = v.ndim = 0``, ``u.ndim = v.ndim = 1``, 

947 ``u.ndim = 1, v.ndim = 0``, and ``u.ndim = 0, v.ndim = 1`` - work. 

948 

949 Examples 

950 -------- 

951 .. math:: \\frac{3x^2 + 5x + 2}{2x + 1} = 1.5x + 1.75, remainder 0.25 

952 

953 >>> x = np.array([3.0, 5.0, 2.0]) 

954 >>> y = np.array([2.0, 1.0]) 

955 >>> np.polydiv(x, y) 

956 (array([1.5 , 1.75]), array([0.25])) 

957 

958 """ 

959 truepoly = (isinstance(u, poly1d) or isinstance(u, poly1d)) 

960 u = atleast_1d(u) + 0.0 

961 v = atleast_1d(v) + 0.0 

962 # w has the common type 

963 w = u[0] + v[0] 

964 m = len(u) - 1 

965 n = len(v) - 1 

966 scale = 1. / v[0] 

967 q = NX.zeros((max(m - n + 1, 1),), w.dtype) 

968 r = u.astype(w.dtype) 

969 for k in range(0, m-n+1): 

970 d = scale * r[k] 

971 q[k] = d 

972 r[k:k+n+1] -= d*v 

973 while NX.allclose(r[0], 0, rtol=1e-14) and (r.shape[-1] > 1): 

974 r = r[1:] 

975 if truepoly: 

976 return poly1d(q), poly1d(r) 

977 return q, r 

978 

979_poly_mat = re.compile(r"[*][*]([0-9]*)") 

980def _raise_power(astr, wrap=70): 

981 n = 0 

982 line1 = '' 

983 line2 = '' 

984 output = ' ' 

985 while True: 

986 mat = _poly_mat.search(astr, n) 

987 if mat is None: 

988 break 

989 span = mat.span() 

990 power = mat.groups()[0] 

991 partstr = astr[n:span[0]] 

992 n = span[1] 

993 toadd2 = partstr + ' '*(len(power)-1) 

994 toadd1 = ' '*(len(partstr)-1) + power 

995 if ((len(line2) + len(toadd2) > wrap) or 

996 (len(line1) + len(toadd1) > wrap)): 

997 output += line1 + "\n" + line2 + "\n " 

998 line1 = toadd1 

999 line2 = toadd2 

1000 else: 

1001 line2 += partstr + ' '*(len(power)-1) 

1002 line1 += ' '*(len(partstr)-1) + power 

1003 output += line1 + "\n" + line2 

1004 return output + astr[n:] 

1005 

1006 

1007@set_module('numpy') 

1008class poly1d: 

1009 """ 

1010 A one-dimensional polynomial class. 

1011 

1012 A convenience class, used to encapsulate "natural" operations on 

1013 polynomials so that said operations may take on their customary 

1014 form in code (see Examples). 

1015 

1016 Parameters 

1017 ---------- 

1018 c_or_r : array_like 

1019 The polynomial's coefficients, in decreasing powers, or if 

1020 the value of the second parameter is True, the polynomial's 

1021 roots (values where the polynomial evaluates to 0). For example, 

1022 ``poly1d([1, 2, 3])`` returns an object that represents 

1023 :math:`x^2 + 2x + 3`, whereas ``poly1d([1, 2, 3], True)`` returns 

1024 one that represents :math:`(x-1)(x-2)(x-3) = x^3 - 6x^2 + 11x -6`. 

1025 r : bool, optional 

1026 If True, `c_or_r` specifies the polynomial's roots; the default 

1027 is False. 

1028 variable : str, optional 

1029 Changes the variable used when printing `p` from `x` to `variable` 

1030 (see Examples). 

1031 

1032 Examples 

1033 -------- 

1034 Construct the polynomial :math:`x^2 + 2x + 3`: 

1035 

1036 >>> p = np.poly1d([1, 2, 3]) 

1037 >>> print(np.poly1d(p)) 

1038 2 

1039 1 x + 2 x + 3 

1040 

1041 Evaluate the polynomial at :math:`x = 0.5`: 

1042 

1043 >>> p(0.5) 

1044 4.25 

1045 

1046 Find the roots: 

1047 

1048 >>> p.r 

1049 array([-1.+1.41421356j, -1.-1.41421356j]) 

1050 >>> p(p.r) 

1051 array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j]) # may vary 

1052 

1053 These numbers in the previous line represent (0, 0) to machine precision 

1054 

1055 Show the coefficients: 

1056 

1057 >>> p.c 

1058 array([1, 2, 3]) 

1059 

1060 Display the order (the leading zero-coefficients are removed): 

1061 

1062 >>> p.order 

1063 2 

1064 

1065 Show the coefficient of the k-th power in the polynomial 

1066 (which is equivalent to ``p.c[-(i+1)]``): 

1067 

1068 >>> p[1] 

1069 2 

1070 

1071 Polynomials can be added, subtracted, multiplied, and divided 

1072 (returns quotient and remainder): 

1073 

1074 >>> p * p 

1075 poly1d([ 1, 4, 10, 12, 9]) 

1076 

1077 >>> (p**3 + 4) / p 

1078 (poly1d([ 1., 4., 10., 12., 9.]), poly1d([4.])) 

1079 

1080 ``asarray(p)`` gives the coefficient array, so polynomials can be 

1081 used in all functions that accept arrays: 

1082 

1083 >>> p**2 # square of polynomial 

1084 poly1d([ 1, 4, 10, 12, 9]) 

1085 

1086 >>> np.square(p) # square of individual coefficients 

1087 array([1, 4, 9]) 

1088 

1089 The variable used in the string representation of `p` can be modified, 

1090 using the `variable` parameter: 

1091 

1092 >>> p = np.poly1d([1,2,3], variable='z') 

1093 >>> print(p) 

1094 2 

1095 1 z + 2 z + 3 

1096 

1097 Construct a polynomial from its roots: 

1098 

1099 >>> np.poly1d([1, 2], True) 

1100 poly1d([ 1., -3., 2.]) 

1101 

1102 This is the same polynomial as obtained by: 

1103 

1104 >>> np.poly1d([1, -1]) * np.poly1d([1, -2]) 

1105 poly1d([ 1, -3, 2]) 

1106 

1107 """ 

1108 __hash__ = None 

1109 

1110 @property 

1111 def coeffs(self): 

1112 """ The polynomial coefficients """ 

1113 return self._coeffs 

1114 

1115 @coeffs.setter 

1116 def coeffs(self, value): 

1117 # allowing this makes p.coeffs *= 2 legal 

1118 if value is not self._coeffs: 

1119 raise AttributeError("Cannot set attribute") 

1120 

1121 @property 

1122 def variable(self): 

1123 """ The name of the polynomial variable """ 

1124 return self._variable 

1125 

1126 # calculated attributes 

1127 @property 

1128 def order(self): 

1129 """ The order or degree of the polynomial """ 

1130 return len(self._coeffs) - 1 

1131 

1132 @property 

1133 def roots(self): 

1134 """ The roots of the polynomial, where self(x) == 0 """ 

1135 return roots(self._coeffs) 

1136 

1137 # our internal _coeffs property need to be backed by __dict__['coeffs'] for 

1138 # scipy to work correctly. 

1139 @property 

1140 def _coeffs(self): 

1141 return self.__dict__['coeffs'] 

1142 @_coeffs.setter 

1143 def _coeffs(self, coeffs): 

1144 self.__dict__['coeffs'] = coeffs 

1145 

1146 # alias attributes 

1147 r = roots 

1148 c = coef = coefficients = coeffs 

1149 o = order 

1150 

1151 def __init__(self, c_or_r, r=False, variable=None): 

1152 if isinstance(c_or_r, poly1d): 

1153 self._variable = c_or_r._variable 

1154 self._coeffs = c_or_r._coeffs 

1155 

1156 if set(c_or_r.__dict__) - set(self.__dict__): 

1157 msg = ("In the future extra properties will not be copied " 

1158 "across when constructing one poly1d from another") 

1159 warnings.warn(msg, FutureWarning, stacklevel=2) 

1160 self.__dict__.update(c_or_r.__dict__) 

1161 

1162 if variable is not None: 

1163 self._variable = variable 

1164 return 

1165 if r: 

1166 c_or_r = poly(c_or_r) 

1167 c_or_r = atleast_1d(c_or_r) 

1168 if c_or_r.ndim > 1: 

1169 raise ValueError("Polynomial must be 1d only.") 

1170 c_or_r = trim_zeros(c_or_r, trim='f') 

1171 if len(c_or_r) == 0: 

1172 c_or_r = NX.array([0.]) 

1173 self._coeffs = c_or_r 

1174 if variable is None: 

1175 variable = 'x' 

1176 self._variable = variable 

1177 

1178 def __array__(self, t=None): 

1179 if t: 

1180 return NX.asarray(self.coeffs, t) 

1181 else: 

1182 return NX.asarray(self.coeffs) 

1183 

1184 def __repr__(self): 

1185 vals = repr(self.coeffs) 

1186 vals = vals[6:-1] 

1187 return "poly1d(%s)" % vals 

1188 

1189 def __len__(self): 

1190 return self.order 

1191 

1192 def __str__(self): 

1193 thestr = "0" 

1194 var = self.variable 

1195 

1196 # Remove leading zeros 

1197 coeffs = self.coeffs[NX.logical_or.accumulate(self.coeffs != 0)] 

1198 N = len(coeffs)-1 

1199 

1200 def fmt_float(q): 

1201 s = '%.4g' % q 

1202 if s.endswith('.0000'): 

1203 s = s[:-5] 

1204 return s 

1205 

1206 for k in range(len(coeffs)): 

1207 if not iscomplex(coeffs[k]): 

1208 coefstr = fmt_float(real(coeffs[k])) 

1209 elif real(coeffs[k]) == 0: 

1210 coefstr = '%sj' % fmt_float(imag(coeffs[k])) 

1211 else: 

1212 coefstr = '(%s + %sj)' % (fmt_float(real(coeffs[k])), 

1213 fmt_float(imag(coeffs[k]))) 

1214 

1215 power = (N-k) 

1216 if power == 0: 

1217 if coefstr != '0': 

1218 newstr = '%s' % (coefstr,) 

1219 else: 

1220 if k == 0: 

1221 newstr = '0' 

1222 else: 

1223 newstr = '' 

1224 elif power == 1: 

1225 if coefstr == '0': 

1226 newstr = '' 

1227 elif coefstr == 'b': 

1228 newstr = var 

1229 else: 

1230 newstr = '%s %s' % (coefstr, var) 

1231 else: 

1232 if coefstr == '0': 

1233 newstr = '' 

1234 elif coefstr == 'b': 

1235 newstr = '%s**%d' % (var, power,) 

1236 else: 

1237 newstr = '%s %s**%d' % (coefstr, var, power) 

1238 

1239 if k > 0: 

1240 if newstr != '': 

1241 if newstr.startswith('-'): 

1242 thestr = "%s - %s" % (thestr, newstr[1:]) 

1243 else: 

1244 thestr = "%s + %s" % (thestr, newstr) 

1245 else: 

1246 thestr = newstr 

1247 return _raise_power(thestr) 

1248 

1249 def __call__(self, val): 

1250 return polyval(self.coeffs, val) 

1251 

1252 def __neg__(self): 

1253 return poly1d(-self.coeffs) 

1254 

1255 def __pos__(self): 

1256 return self 

1257 

1258 def __mul__(self, other): 

1259 if isscalar(other): 

1260 return poly1d(self.coeffs * other) 

1261 else: 

1262 other = poly1d(other) 

1263 return poly1d(polymul(self.coeffs, other.coeffs)) 

1264 

1265 def __rmul__(self, other): 

1266 if isscalar(other): 

1267 return poly1d(other * self.coeffs) 

1268 else: 

1269 other = poly1d(other) 

1270 return poly1d(polymul(self.coeffs, other.coeffs)) 

1271 

1272 def __add__(self, other): 

1273 other = poly1d(other) 

1274 return poly1d(polyadd(self.coeffs, other.coeffs)) 

1275 

1276 def __radd__(self, other): 

1277 other = poly1d(other) 

1278 return poly1d(polyadd(self.coeffs, other.coeffs)) 

1279 

1280 def __pow__(self, val): 

1281 if not isscalar(val) or int(val) != val or val < 0: 

1282 raise ValueError("Power to non-negative integers only.") 

1283 res = [1] 

1284 for _ in range(val): 

1285 res = polymul(self.coeffs, res) 

1286 return poly1d(res) 

1287 

1288 def __sub__(self, other): 

1289 other = poly1d(other) 

1290 return poly1d(polysub(self.coeffs, other.coeffs)) 

1291 

1292 def __rsub__(self, other): 

1293 other = poly1d(other) 

1294 return poly1d(polysub(other.coeffs, self.coeffs)) 

1295 

1296 def __div__(self, other): 

1297 if isscalar(other): 

1298 return poly1d(self.coeffs/other) 

1299 else: 

1300 other = poly1d(other) 

1301 return polydiv(self, other) 

1302 

1303 __truediv__ = __div__ 

1304 

1305 def __rdiv__(self, other): 

1306 if isscalar(other): 

1307 return poly1d(other/self.coeffs) 

1308 else: 

1309 other = poly1d(other) 

1310 return polydiv(other, self) 

1311 

1312 __rtruediv__ = __rdiv__ 

1313 

1314 def __eq__(self, other): 

1315 if not isinstance(other, poly1d): 

1316 return NotImplemented 

1317 if self.coeffs.shape != other.coeffs.shape: 

1318 return False 

1319 return (self.coeffs == other.coeffs).all() 

1320 

1321 def __ne__(self, other): 

1322 if not isinstance(other, poly1d): 

1323 return NotImplemented 

1324 return not self.__eq__(other) 

1325 

1326 

1327 def __getitem__(self, val): 

1328 ind = self.order - val 

1329 if val > self.order: 

1330 return 0 

1331 if val < 0: 

1332 return 0 

1333 return self.coeffs[ind] 

1334 

1335 def __setitem__(self, key, val): 

1336 ind = self.order - key 

1337 if key < 0: 

1338 raise ValueError("Does not support negative powers.") 

1339 if key > self.order: 

1340 zr = NX.zeros(key-self.order, self.coeffs.dtype) 

1341 self._coeffs = NX.concatenate((zr, self.coeffs)) 

1342 ind = 0 

1343 self._coeffs[ind] = val 

1344 return 

1345 

1346 def __iter__(self): 

1347 return iter(self.coeffs) 

1348 

1349 def integ(self, m=1, k=0): 

1350 """ 

1351 Return an antiderivative (indefinite integral) of this polynomial. 

1352 

1353 Refer to `polyint` for full documentation. 

1354 

1355 See Also 

1356 -------- 

1357 polyint : equivalent function 

1358 

1359 """ 

1360 return poly1d(polyint(self.coeffs, m=m, k=k)) 

1361 

1362 def deriv(self, m=1): 

1363 """ 

1364 Return a derivative of this polynomial. 

1365 

1366 Refer to `polyder` for full documentation. 

1367 

1368 See Also 

1369 -------- 

1370 polyder : equivalent function 

1371 

1372 """ 

1373 return poly1d(polyder(self.coeffs, m=m)) 

1374 

1375# Stuff to do on module import 

1376 

1377warnings.simplefilter('always', RankWarning)