Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/numpy/polynomial/_polybase.py: 29%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

431 statements  

1""" 

2Abstract base class for the various polynomial Classes. 

3 

4The ABCPolyBase class provides the methods needed to implement the common API 

5for the various polynomial classes. It operates as a mixin, but uses the 

6abc module from the stdlib, hence it is only available for Python >= 2.6. 

7 

8""" 

9import os 

10import abc 

11import numbers 

12 

13import numpy as np 

14from . import polyutils as pu 

15 

16__all__ = ['ABCPolyBase'] 

17 

18class ABCPolyBase(abc.ABC): 

19 """An abstract base class for immutable series classes. 

20 

21 ABCPolyBase provides the standard Python numerical methods 

22 '+', '-', '*', '//', '%', 'divmod', '**', and '()' along with the 

23 methods listed below. 

24 

25 .. versionadded:: 1.9.0 

26 

27 Parameters 

28 ---------- 

29 coef : array_like 

30 Series coefficients in order of increasing degree, i.e., 

31 ``(1, 2, 3)`` gives ``1*P_0(x) + 2*P_1(x) + 3*P_2(x)``, where 

32 ``P_i`` is the basis polynomials of degree ``i``. 

33 domain : (2,) array_like, optional 

34 Domain to use. The interval ``[domain[0], domain[1]]`` is mapped 

35 to the interval ``[window[0], window[1]]`` by shifting and scaling. 

36 The default value is the derived class domain. 

37 window : (2,) array_like, optional 

38 Window, see domain for its use. The default value is the 

39 derived class window. 

40 symbol : str, optional 

41 Symbol used to represent the independent variable in string  

42 representations of the polynomial expression, e.g. for printing. 

43 The symbol must be a valid Python identifier. Default value is 'x'. 

44 

45 .. versionadded:: 1.24 

46 

47 Attributes 

48 ---------- 

49 coef : (N,) ndarray 

50 Series coefficients in order of increasing degree. 

51 domain : (2,) ndarray 

52 Domain that is mapped to window. 

53 window : (2,) ndarray 

54 Window that domain is mapped to. 

55 symbol : str 

56 Symbol representing the independent variable. 

57 

58 Class Attributes 

59 ---------------- 

60 maxpower : int 

61 Maximum power allowed, i.e., the largest number ``n`` such that 

62 ``p(x)**n`` is allowed. This is to limit runaway polynomial size. 

63 domain : (2,) ndarray 

64 Default domain of the class. 

65 window : (2,) ndarray 

66 Default window of the class. 

67 

68 """ 

69 

70 # Not hashable 

71 __hash__ = None 

72 

73 # Opt out of numpy ufuncs and Python ops with ndarray subclasses. 

74 __array_ufunc__ = None 

75 

76 # Limit runaway size. T_n^m has degree n*m 

77 maxpower = 100 

78 

79 # Unicode character mappings for improved __str__ 

80 _superscript_mapping = str.maketrans({ 

81 "0": "⁰", 

82 "1": "¹", 

83 "2": "²", 

84 "3": "³", 

85 "4": "⁴", 

86 "5": "⁵", 

87 "6": "⁶", 

88 "7": "⁷", 

89 "8": "⁸", 

90 "9": "⁹" 

91 }) 

92 _subscript_mapping = str.maketrans({ 

93 "0": "₀", 

94 "1": "₁", 

95 "2": "₂", 

96 "3": "₃", 

97 "4": "₄", 

98 "5": "₅", 

99 "6": "₆", 

100 "7": "₇", 

101 "8": "₈", 

102 "9": "₉" 

103 }) 

104 # Some fonts don't support full unicode character ranges necessary for 

105 # the full set of superscripts and subscripts, including common/default 

106 # fonts in Windows shells/terminals. Therefore, default to ascii-only 

107 # printing on windows. 

108 _use_unicode = not os.name == 'nt' 

109 

110 @property 

111 def symbol(self): 

112 return self._symbol 

113 

114 @property 

115 @abc.abstractmethod 

116 def domain(self): 

117 pass 

118 

119 @property 

120 @abc.abstractmethod 

121 def window(self): 

122 pass 

123 

124 @property 

125 @abc.abstractmethod 

126 def basis_name(self): 

127 pass 

128 

129 @staticmethod 

130 @abc.abstractmethod 

131 def _add(c1, c2): 

132 pass 

133 

134 @staticmethod 

135 @abc.abstractmethod 

136 def _sub(c1, c2): 

137 pass 

138 

139 @staticmethod 

140 @abc.abstractmethod 

141 def _mul(c1, c2): 

142 pass 

143 

144 @staticmethod 

145 @abc.abstractmethod 

146 def _div(c1, c2): 

147 pass 

148 

149 @staticmethod 

150 @abc.abstractmethod 

151 def _pow(c, pow, maxpower=None): 

152 pass 

153 

154 @staticmethod 

155 @abc.abstractmethod 

156 def _val(x, c): 

157 pass 

158 

159 @staticmethod 

160 @abc.abstractmethod 

161 def _int(c, m, k, lbnd, scl): 

162 pass 

163 

164 @staticmethod 

165 @abc.abstractmethod 

166 def _der(c, m, scl): 

167 pass 

168 

169 @staticmethod 

170 @abc.abstractmethod 

171 def _fit(x, y, deg, rcond, full): 

172 pass 

173 

174 @staticmethod 

175 @abc.abstractmethod 

176 def _line(off, scl): 

177 pass 

178 

179 @staticmethod 

180 @abc.abstractmethod 

181 def _roots(c): 

182 pass 

183 

184 @staticmethod 

185 @abc.abstractmethod 

186 def _fromroots(r): 

187 pass 

188 

189 def has_samecoef(self, other): 

190 """Check if coefficients match. 

191 

192 .. versionadded:: 1.6.0 

193 

194 Parameters 

195 ---------- 

196 other : class instance 

197 The other class must have the ``coef`` attribute. 

198 

199 Returns 

200 ------- 

201 bool : boolean 

202 True if the coefficients are the same, False otherwise. 

203 

204 """ 

205 if len(self.coef) != len(other.coef): 

206 return False 

207 elif not np.all(self.coef == other.coef): 

208 return False 

209 else: 

210 return True 

211 

212 def has_samedomain(self, other): 

213 """Check if domains match. 

214 

215 .. versionadded:: 1.6.0 

216 

217 Parameters 

218 ---------- 

219 other : class instance 

220 The other class must have the ``domain`` attribute. 

221 

222 Returns 

223 ------- 

224 bool : boolean 

225 True if the domains are the same, False otherwise. 

226 

227 """ 

228 return np.all(self.domain == other.domain) 

229 

230 def has_samewindow(self, other): 

231 """Check if windows match. 

232 

233 .. versionadded:: 1.6.0 

234 

235 Parameters 

236 ---------- 

237 other : class instance 

238 The other class must have the ``window`` attribute. 

239 

240 Returns 

241 ------- 

242 bool : boolean 

243 True if the windows are the same, False otherwise. 

244 

245 """ 

246 return np.all(self.window == other.window) 

247 

248 def has_sametype(self, other): 

249 """Check if types match. 

250 

251 .. versionadded:: 1.7.0 

252 

253 Parameters 

254 ---------- 

255 other : object 

256 Class instance. 

257 

258 Returns 

259 ------- 

260 bool : boolean 

261 True if other is same class as self 

262 

263 """ 

264 return isinstance(other, self.__class__) 

265 

266 def _get_coefficients(self, other): 

267 """Interpret other as polynomial coefficients. 

268 

269 The `other` argument is checked to see if it is of the same 

270 class as self with identical domain and window. If so, 

271 return its coefficients, otherwise return `other`. 

272 

273 .. versionadded:: 1.9.0 

274 

275 Parameters 

276 ---------- 

277 other : anything 

278 Object to be checked. 

279 

280 Returns 

281 ------- 

282 coef 

283 The coefficients of`other` if it is a compatible instance, 

284 of ABCPolyBase, otherwise `other`. 

285 

286 Raises 

287 ------ 

288 TypeError 

289 When `other` is an incompatible instance of ABCPolyBase. 

290 

291 """ 

292 if isinstance(other, ABCPolyBase): 

293 if not isinstance(other, self.__class__): 

294 raise TypeError("Polynomial types differ") 

295 elif not np.all(self.domain == other.domain): 

296 raise TypeError("Domains differ") 

297 elif not np.all(self.window == other.window): 

298 raise TypeError("Windows differ") 

299 elif self.symbol != other.symbol: 

300 raise ValueError("Polynomial symbols differ") 

301 return other.coef 

302 return other 

303 

304 def __init__(self, coef, domain=None, window=None, symbol='x'): 

305 [coef] = pu.as_series([coef], trim=False) 

306 self.coef = coef 

307 

308 if domain is not None: 

309 [domain] = pu.as_series([domain], trim=False) 

310 if len(domain) != 2: 

311 raise ValueError("Domain has wrong number of elements.") 

312 self.domain = domain 

313 

314 if window is not None: 

315 [window] = pu.as_series([window], trim=False) 

316 if len(window) != 2: 

317 raise ValueError("Window has wrong number of elements.") 

318 self.window = window 

319 

320 # Validation for symbol 

321 try: 

322 if not symbol.isidentifier(): 

323 raise ValueError( 

324 "Symbol string must be a valid Python identifier" 

325 ) 

326 # If a user passes in something other than a string, the above 

327 # results in an AttributeError. Catch this and raise a more 

328 # informative exception 

329 except AttributeError: 

330 raise TypeError("Symbol must be a non-empty string") 

331 

332 self._symbol = symbol 

333 

334 def __repr__(self): 

335 coef = repr(self.coef)[6:-1] 

336 domain = repr(self.domain)[6:-1] 

337 window = repr(self.window)[6:-1] 

338 name = self.__class__.__name__ 

339 return (f"{name}({coef}, domain={domain}, window={window}, " 

340 f"symbol='{self.symbol}')") 

341 

342 def __format__(self, fmt_str): 

343 if fmt_str == '': 

344 return self.__str__() 

345 if fmt_str not in ('ascii', 'unicode'): 

346 raise ValueError( 

347 f"Unsupported format string '{fmt_str}' passed to " 

348 f"{self.__class__}.__format__. Valid options are " 

349 f"'ascii' and 'unicode'" 

350 ) 

351 if fmt_str == 'ascii': 

352 return self._generate_string(self._str_term_ascii) 

353 return self._generate_string(self._str_term_unicode) 

354 

355 def __str__(self): 

356 if self._use_unicode: 

357 return self._generate_string(self._str_term_unicode) 

358 return self._generate_string(self._str_term_ascii) 

359 

360 def _generate_string(self, term_method): 

361 """ 

362 Generate the full string representation of the polynomial, using 

363 ``term_method`` to generate each polynomial term. 

364 """ 

365 # Get configuration for line breaks 

366 linewidth = np.get_printoptions().get('linewidth', 75) 

367 if linewidth < 1: 

368 linewidth = 1 

369 out = pu.format_float(self.coef[0]) 

370 for i, coef in enumerate(self.coef[1:]): 

371 out += " " 

372 power = str(i + 1) 

373 # Polynomial coefficient 

374 # The coefficient array can be an object array with elements that 

375 # will raise a TypeError with >= 0 (e.g. strings or Python 

376 # complex). In this case, represent the coefficient as-is. 

377 try: 

378 if coef >= 0: 

379 next_term = f"+ " + pu.format_float(coef, parens=True) 

380 else: 

381 next_term = f"- " + pu.format_float(-coef, parens=True) 

382 except TypeError: 

383 next_term = f"+ {coef}" 

384 # Polynomial term 

385 next_term += term_method(power, self.symbol) 

386 # Length of the current line with next term added 

387 line_len = len(out.split('\n')[-1]) + len(next_term) 

388 # If not the last term in the polynomial, it will be two 

389 # characters longer due to the +/- with the next term 

390 if i < len(self.coef[1:]) - 1: 

391 line_len += 2 

392 # Handle linebreaking 

393 if line_len >= linewidth: 

394 next_term = next_term.replace(" ", "\n", 1) 

395 out += next_term 

396 return out 

397 

398 @classmethod 

399 def _str_term_unicode(cls, i, arg_str): 

400 """ 

401 String representation of single polynomial term using unicode 

402 characters for superscripts and subscripts. 

403 """ 

404 if cls.basis_name is None: 

405 raise NotImplementedError( 

406 "Subclasses must define either a basis_name, or override " 

407 "_str_term_unicode(cls, i, arg_str)" 

408 ) 

409 return (f"·{cls.basis_name}{i.translate(cls._subscript_mapping)}" 

410 f"({arg_str})") 

411 

412 @classmethod 

413 def _str_term_ascii(cls, i, arg_str): 

414 """ 

415 String representation of a single polynomial term using ** and _ to 

416 represent superscripts and subscripts, respectively. 

417 """ 

418 if cls.basis_name is None: 

419 raise NotImplementedError( 

420 "Subclasses must define either a basis_name, or override " 

421 "_str_term_ascii(cls, i, arg_str)" 

422 ) 

423 return f" {cls.basis_name}_{i}({arg_str})" 

424 

425 @classmethod 

426 def _repr_latex_term(cls, i, arg_str, needs_parens): 

427 if cls.basis_name is None: 

428 raise NotImplementedError( 

429 "Subclasses must define either a basis name, or override " 

430 "_repr_latex_term(i, arg_str, needs_parens)") 

431 # since we always add parens, we don't care if the expression needs them 

432 return f"{{{cls.basis_name}}}_{{{i}}}({arg_str})" 

433 

434 @staticmethod 

435 def _repr_latex_scalar(x, parens=False): 

436 # TODO: we're stuck with disabling math formatting until we handle 

437 # exponents in this function 

438 return r'\text{{{}}}'.format(pu.format_float(x, parens=parens)) 

439 

440 def _repr_latex_(self): 

441 # get the scaled argument string to the basis functions 

442 off, scale = self.mapparms() 

443 if off == 0 and scale == 1: 

444 term = self.symbol 

445 needs_parens = False 

446 elif scale == 1: 

447 term = f"{self._repr_latex_scalar(off)} + {self.symbol}" 

448 needs_parens = True 

449 elif off == 0: 

450 term = f"{self._repr_latex_scalar(scale)}{self.symbol}" 

451 needs_parens = True 

452 else: 

453 term = ( 

454 f"{self._repr_latex_scalar(off)} + " 

455 f"{self._repr_latex_scalar(scale)}{self.symbol}" 

456 ) 

457 needs_parens = True 

458 

459 mute = r"\color{{LightGray}}{{{}}}".format 

460 

461 parts = [] 

462 for i, c in enumerate(self.coef): 

463 # prevent duplication of + and - signs 

464 if i == 0: 

465 coef_str = f"{self._repr_latex_scalar(c)}" 

466 elif not isinstance(c, numbers.Real): 

467 coef_str = f" + ({self._repr_latex_scalar(c)})" 

468 elif not np.signbit(c): 

469 coef_str = f" + {self._repr_latex_scalar(c, parens=True)}" 

470 else: 

471 coef_str = f" - {self._repr_latex_scalar(-c, parens=True)}" 

472 

473 # produce the string for the term 

474 term_str = self._repr_latex_term(i, term, needs_parens) 

475 if term_str == '1': 

476 part = coef_str 

477 else: 

478 part = rf"{coef_str}\,{term_str}" 

479 

480 if c == 0: 

481 part = mute(part) 

482 

483 parts.append(part) 

484 

485 if parts: 

486 body = ''.join(parts) 

487 else: 

488 # in case somehow there are no coefficients at all 

489 body = '0' 

490 

491 return rf"${self.symbol} \mapsto {body}$" 

492 

493 

494 

495 # Pickle and copy 

496 

497 def __getstate__(self): 

498 ret = self.__dict__.copy() 

499 ret['coef'] = self.coef.copy() 

500 ret['domain'] = self.domain.copy() 

501 ret['window'] = self.window.copy() 

502 ret['symbol'] = self.symbol 

503 return ret 

504 

505 def __setstate__(self, dict): 

506 self.__dict__ = dict 

507 

508 # Call 

509 

510 def __call__(self, arg): 

511 off, scl = pu.mapparms(self.domain, self.window) 

512 arg = off + scl*arg 

513 return self._val(arg, self.coef) 

514 

515 def __iter__(self): 

516 return iter(self.coef) 

517 

518 def __len__(self): 

519 return len(self.coef) 

520 

521 # Numeric properties. 

522 

523 def __neg__(self): 

524 return self.__class__( 

525 -self.coef, self.domain, self.window, self.symbol 

526 ) 

527 

528 def __pos__(self): 

529 return self 

530 

531 def __add__(self, other): 

532 othercoef = self._get_coefficients(other) 

533 try: 

534 coef = self._add(self.coef, othercoef) 

535 except Exception: 

536 return NotImplemented 

537 return self.__class__(coef, self.domain, self.window, self.symbol) 

538 

539 def __sub__(self, other): 

540 othercoef = self._get_coefficients(other) 

541 try: 

542 coef = self._sub(self.coef, othercoef) 

543 except Exception: 

544 return NotImplemented 

545 return self.__class__(coef, self.domain, self.window, self.symbol) 

546 

547 def __mul__(self, other): 

548 othercoef = self._get_coefficients(other) 

549 try: 

550 coef = self._mul(self.coef, othercoef) 

551 except Exception: 

552 return NotImplemented 

553 return self.__class__(coef, self.domain, self.window, self.symbol) 

554 

555 def __truediv__(self, other): 

556 # there is no true divide if the rhs is not a Number, although it 

557 # could return the first n elements of an infinite series. 

558 # It is hard to see where n would come from, though. 

559 if not isinstance(other, numbers.Number) or isinstance(other, bool): 

560 raise TypeError( 

561 f"unsupported types for true division: " 

562 f"'{type(self)}', '{type(other)}'" 

563 ) 

564 return self.__floordiv__(other) 

565 

566 def __floordiv__(self, other): 

567 res = self.__divmod__(other) 

568 if res is NotImplemented: 

569 return res 

570 return res[0] 

571 

572 def __mod__(self, other): 

573 res = self.__divmod__(other) 

574 if res is NotImplemented: 

575 return res 

576 return res[1] 

577 

578 def __divmod__(self, other): 

579 othercoef = self._get_coefficients(other) 

580 try: 

581 quo, rem = self._div(self.coef, othercoef) 

582 except ZeroDivisionError: 

583 raise 

584 except Exception: 

585 return NotImplemented 

586 quo = self.__class__(quo, self.domain, self.window, self.symbol) 

587 rem = self.__class__(rem, self.domain, self.window, self.symbol) 

588 return quo, rem 

589 

590 def __pow__(self, other): 

591 coef = self._pow(self.coef, other, maxpower=self.maxpower) 

592 res = self.__class__(coef, self.domain, self.window, self.symbol) 

593 return res 

594 

595 def __radd__(self, other): 

596 try: 

597 coef = self._add(other, self.coef) 

598 except Exception: 

599 return NotImplemented 

600 return self.__class__(coef, self.domain, self.window, self.symbol) 

601 

602 def __rsub__(self, other): 

603 try: 

604 coef = self._sub(other, self.coef) 

605 except Exception: 

606 return NotImplemented 

607 return self.__class__(coef, self.domain, self.window, self.symbol) 

608 

609 def __rmul__(self, other): 

610 try: 

611 coef = self._mul(other, self.coef) 

612 except Exception: 

613 return NotImplemented 

614 return self.__class__(coef, self.domain, self.window, self.symbol) 

615 

616 def __rdiv__(self, other): 

617 # set to __floordiv__ /. 

618 return self.__rfloordiv__(other) 

619 

620 def __rtruediv__(self, other): 

621 # An instance of ABCPolyBase is not considered a 

622 # Number. 

623 return NotImplemented 

624 

625 def __rfloordiv__(self, other): 

626 res = self.__rdivmod__(other) 

627 if res is NotImplemented: 

628 return res 

629 return res[0] 

630 

631 def __rmod__(self, other): 

632 res = self.__rdivmod__(other) 

633 if res is NotImplemented: 

634 return res 

635 return res[1] 

636 

637 def __rdivmod__(self, other): 

638 try: 

639 quo, rem = self._div(other, self.coef) 

640 except ZeroDivisionError: 

641 raise 

642 except Exception: 

643 return NotImplemented 

644 quo = self.__class__(quo, self.domain, self.window, self.symbol) 

645 rem = self.__class__(rem, self.domain, self.window, self.symbol) 

646 return quo, rem 

647 

648 def __eq__(self, other): 

649 res = (isinstance(other, self.__class__) and 

650 np.all(self.domain == other.domain) and 

651 np.all(self.window == other.window) and 

652 (self.coef.shape == other.coef.shape) and 

653 np.all(self.coef == other.coef) and 

654 (self.symbol == other.symbol)) 

655 return res 

656 

657 def __ne__(self, other): 

658 return not self.__eq__(other) 

659 

660 # 

661 # Extra methods. 

662 # 

663 

664 def copy(self): 

665 """Return a copy. 

666 

667 Returns 

668 ------- 

669 new_series : series 

670 Copy of self. 

671 

672 """ 

673 return self.__class__(self.coef, self.domain, self.window, self.symbol) 

674 

675 def degree(self): 

676 """The degree of the series. 

677 

678 .. versionadded:: 1.5.0 

679 

680 Returns 

681 ------- 

682 degree : int 

683 Degree of the series, one less than the number of coefficients. 

684 

685 Examples 

686 -------- 

687 

688 Create a polynomial object for ``1 + 7*x + 4*x**2``: 

689 

690 >>> poly = np.polynomial.Polynomial([1, 7, 4]) 

691 >>> print(poly) 

692 1.0 + 7.0·x + 4.0·x² 

693 >>> poly.degree() 

694 2 

695 

696 Note that this method does not check for non-zero coefficients. 

697 You must trim the polynomial to remove any trailing zeroes: 

698 

699 >>> poly = np.polynomial.Polynomial([1, 7, 0]) 

700 >>> print(poly) 

701 1.0 + 7.0·x + 0.0·x² 

702 >>> poly.degree() 

703 2 

704 >>> poly.trim().degree() 

705 1 

706 

707 """ 

708 return len(self) - 1 

709 

710 def cutdeg(self, deg): 

711 """Truncate series to the given degree. 

712 

713 Reduce the degree of the series to `deg` by discarding the 

714 high order terms. If `deg` is greater than the current degree a 

715 copy of the current series is returned. This can be useful in least 

716 squares where the coefficients of the high degree terms may be very 

717 small. 

718 

719 .. versionadded:: 1.5.0 

720 

721 Parameters 

722 ---------- 

723 deg : non-negative int 

724 The series is reduced to degree `deg` by discarding the high 

725 order terms. The value of `deg` must be a non-negative integer. 

726 

727 Returns 

728 ------- 

729 new_series : series 

730 New instance of series with reduced degree. 

731 

732 """ 

733 return self.truncate(deg + 1) 

734 

735 def trim(self, tol=0): 

736 """Remove trailing coefficients 

737 

738 Remove trailing coefficients until a coefficient is reached whose 

739 absolute value greater than `tol` or the beginning of the series is 

740 reached. If all the coefficients would be removed the series is set 

741 to ``[0]``. A new series instance is returned with the new 

742 coefficients. The current instance remains unchanged. 

743 

744 Parameters 

745 ---------- 

746 tol : non-negative number. 

747 All trailing coefficients less than `tol` will be removed. 

748 

749 Returns 

750 ------- 

751 new_series : series 

752 New instance of series with trimmed coefficients. 

753 

754 """ 

755 coef = pu.trimcoef(self.coef, tol) 

756 return self.__class__(coef, self.domain, self.window, self.symbol) 

757 

758 def truncate(self, size): 

759 """Truncate series to length `size`. 

760 

761 Reduce the series to length `size` by discarding the high 

762 degree terms. The value of `size` must be a positive integer. This 

763 can be useful in least squares where the coefficients of the 

764 high degree terms may be very small. 

765 

766 Parameters 

767 ---------- 

768 size : positive int 

769 The series is reduced to length `size` by discarding the high 

770 degree terms. The value of `size` must be a positive integer. 

771 

772 Returns 

773 ------- 

774 new_series : series 

775 New instance of series with truncated coefficients. 

776 

777 """ 

778 isize = int(size) 

779 if isize != size or isize < 1: 

780 raise ValueError("size must be a positive integer") 

781 if isize >= len(self.coef): 

782 coef = self.coef 

783 else: 

784 coef = self.coef[:isize] 

785 return self.__class__(coef, self.domain, self.window, self.symbol) 

786 

787 def convert(self, domain=None, kind=None, window=None): 

788 """Convert series to a different kind and/or domain and/or window. 

789 

790 Parameters 

791 ---------- 

792 domain : array_like, optional 

793 The domain of the converted series. If the value is None, 

794 the default domain of `kind` is used. 

795 kind : class, optional 

796 The polynomial series type class to which the current instance 

797 should be converted. If kind is None, then the class of the 

798 current instance is used. 

799 window : array_like, optional 

800 The window of the converted series. If the value is None, 

801 the default window of `kind` is used. 

802 

803 Returns 

804 ------- 

805 new_series : series 

806 The returned class can be of different type than the current 

807 instance and/or have a different domain and/or different 

808 window. 

809 

810 Notes 

811 ----- 

812 Conversion between domains and class types can result in 

813 numerically ill defined series. 

814 

815 """ 

816 if kind is None: 

817 kind = self.__class__ 

818 if domain is None: 

819 domain = kind.domain 

820 if window is None: 

821 window = kind.window 

822 return self(kind.identity(domain, window=window, symbol=self.symbol)) 

823 

824 def mapparms(self): 

825 """Return the mapping parameters. 

826 

827 The returned values define a linear map ``off + scl*x`` that is 

828 applied to the input arguments before the series is evaluated. The 

829 map depends on the ``domain`` and ``window``; if the current 

830 ``domain`` is equal to the ``window`` the resulting map is the 

831 identity. If the coefficients of the series instance are to be 

832 used by themselves outside this class, then the linear function 

833 must be substituted for the ``x`` in the standard representation of 

834 the base polynomials. 

835 

836 Returns 

837 ------- 

838 off, scl : float or complex 

839 The mapping function is defined by ``off + scl*x``. 

840 

841 Notes 

842 ----- 

843 If the current domain is the interval ``[l1, r1]`` and the window 

844 is ``[l2, r2]``, then the linear mapping function ``L`` is 

845 defined by the equations:: 

846 

847 L(l1) = l2 

848 L(r1) = r2 

849 

850 """ 

851 return pu.mapparms(self.domain, self.window) 

852 

853 def integ(self, m=1, k=[], lbnd=None): 

854 """Integrate. 

855 

856 Return a series instance that is the definite integral of the 

857 current series. 

858 

859 Parameters 

860 ---------- 

861 m : non-negative int 

862 The number of integrations to perform. 

863 k : array_like 

864 Integration constants. The first constant is applied to the 

865 first integration, the second to the second, and so on. The 

866 list of values must less than or equal to `m` in length and any 

867 missing values are set to zero. 

868 lbnd : Scalar 

869 The lower bound of the definite integral. 

870 

871 Returns 

872 ------- 

873 new_series : series 

874 A new series representing the integral. The domain is the same 

875 as the domain of the integrated series. 

876 

877 """ 

878 off, scl = self.mapparms() 

879 if lbnd is None: 

880 lbnd = 0 

881 else: 

882 lbnd = off + scl*lbnd 

883 coef = self._int(self.coef, m, k, lbnd, 1./scl) 

884 return self.__class__(coef, self.domain, self.window, self.symbol) 

885 

886 def deriv(self, m=1): 

887 """Differentiate. 

888 

889 Return a series instance of that is the derivative of the current 

890 series. 

891 

892 Parameters 

893 ---------- 

894 m : non-negative int 

895 Find the derivative of order `m`. 

896 

897 Returns 

898 ------- 

899 new_series : series 

900 A new series representing the derivative. The domain is the same 

901 as the domain of the differentiated series. 

902 

903 """ 

904 off, scl = self.mapparms() 

905 coef = self._der(self.coef, m, scl) 

906 return self.__class__(coef, self.domain, self.window, self.symbol) 

907 

908 def roots(self): 

909 """Return the roots of the series polynomial. 

910 

911 Compute the roots for the series. Note that the accuracy of the 

912 roots decreases the further outside the `domain` they lie. 

913 

914 Returns 

915 ------- 

916 roots : ndarray 

917 Array containing the roots of the series. 

918 

919 """ 

920 roots = self._roots(self.coef) 

921 return pu.mapdomain(roots, self.window, self.domain) 

922 

923 def linspace(self, n=100, domain=None): 

924 """Return x, y values at equally spaced points in domain. 

925 

926 Returns the x, y values at `n` linearly spaced points across the 

927 domain. Here y is the value of the polynomial at the points x. By 

928 default the domain is the same as that of the series instance. 

929 This method is intended mostly as a plotting aid. 

930 

931 .. versionadded:: 1.5.0 

932 

933 Parameters 

934 ---------- 

935 n : int, optional 

936 Number of point pairs to return. The default value is 100. 

937 domain : {None, array_like}, optional 

938 If not None, the specified domain is used instead of that of 

939 the calling instance. It should be of the form ``[beg,end]``. 

940 The default is None which case the class domain is used. 

941 

942 Returns 

943 ------- 

944 x, y : ndarray 

945 x is equal to linspace(self.domain[0], self.domain[1], n) and 

946 y is the series evaluated at element of x. 

947 

948 """ 

949 if domain is None: 

950 domain = self.domain 

951 x = np.linspace(domain[0], domain[1], n) 

952 y = self(x) 

953 return x, y 

954 

955 @classmethod 

956 def fit(cls, x, y, deg, domain=None, rcond=None, full=False, w=None, 

957 window=None, symbol='x'): 

958 """Least squares fit to data. 

959 

960 Return a series instance that is the least squares fit to the data 

961 `y` sampled at `x`. The domain of the returned instance can be 

962 specified and this will often result in a superior fit with less 

963 chance of ill conditioning. 

964 

965 Parameters 

966 ---------- 

967 x : array_like, shape (M,) 

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

969 y : array_like, shape (M,) 

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

971 deg : int or 1-D array_like 

972 Degree(s) of the fitting polynomials. If `deg` is a single integer 

973 all terms up to and including the `deg`'th term are included in the 

974 fit. For NumPy versions >= 1.11.0 a list of integers specifying the 

975 degrees of the terms to include may be used instead. 

976 domain : {None, [beg, end], []}, optional 

977 Domain to use for the returned series. If ``None``, 

978 then a minimal domain that covers the points `x` is chosen. If 

979 ``[]`` the class domain is used. The default value was the 

980 class domain in NumPy 1.4 and ``None`` in later versions. 

981 The ``[]`` option was added in numpy 1.5.0. 

982 rcond : float, optional 

983 Relative condition number of the fit. Singular values smaller 

984 than this relative to the largest singular value will be 

985 ignored. The default value is len(x)*eps, where eps is the 

986 relative precision of the float type, about 2e-16 in most 

987 cases. 

988 full : bool, optional 

989 Switch determining nature of return value. When it is False 

990 (the default) just the coefficients are returned, when True 

991 diagnostic information from the singular value decomposition is 

992 also returned. 

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

994 Weights. If not None, the weight ``w[i]`` applies to the unsquared 

995 residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are 

996 chosen so that the errors of the products ``w[i]*y[i]`` all have 

997 the same variance. When using inverse-variance weighting, use 

998 ``w[i] = 1/sigma(y[i])``. The default value is None. 

999 

1000 .. versionadded:: 1.5.0 

1001 window : {[beg, end]}, optional 

1002 Window to use for the returned series. The default 

1003 value is the default class domain 

1004 

1005 .. versionadded:: 1.6.0 

1006 symbol : str, optional 

1007 Symbol representing the independent variable. Default is 'x'. 

1008 

1009 Returns 

1010 ------- 

1011 new_series : series 

1012 A series that represents the least squares fit to the data and 

1013 has the domain and window specified in the call. If the 

1014 coefficients for the unscaled and unshifted basis polynomials are 

1015 of interest, do ``new_series.convert().coef``. 

1016 

1017 [resid, rank, sv, rcond] : list 

1018 These values are only returned if ``full == True`` 

1019 

1020 - resid -- sum of squared residuals of the least squares fit 

1021 - rank -- the numerical rank of the scaled Vandermonde matrix 

1022 - sv -- singular values of the scaled Vandermonde matrix 

1023 - rcond -- value of `rcond`. 

1024 

1025 For more details, see `linalg.lstsq`. 

1026 

1027 """ 

1028 if domain is None: 

1029 domain = pu.getdomain(x) 

1030 elif type(domain) is list and len(domain) == 0: 

1031 domain = cls.domain 

1032 

1033 if window is None: 

1034 window = cls.window 

1035 

1036 xnew = pu.mapdomain(x, domain, window) 

1037 res = cls._fit(xnew, y, deg, w=w, rcond=rcond, full=full) 

1038 if full: 

1039 [coef, status] = res 

1040 return ( 

1041 cls(coef, domain=domain, window=window, symbol=symbol), status 

1042 ) 

1043 else: 

1044 coef = res 

1045 return cls(coef, domain=domain, window=window, symbol=symbol) 

1046 

1047 @classmethod 

1048 def fromroots(cls, roots, domain=[], window=None, symbol='x'): 

1049 """Return series instance that has the specified roots. 

1050 

1051 Returns a series representing the product 

1052 ``(x - r[0])*(x - r[1])*...*(x - r[n-1])``, where ``r`` is a 

1053 list of roots. 

1054 

1055 Parameters 

1056 ---------- 

1057 roots : array_like 

1058 List of roots. 

1059 domain : {[], None, array_like}, optional 

1060 Domain for the resulting series. If None the domain is the 

1061 interval from the smallest root to the largest. If [] the 

1062 domain is the class domain. The default is []. 

1063 window : {None, array_like}, optional 

1064 Window for the returned series. If None the class window is 

1065 used. The default is None. 

1066 symbol : str, optional 

1067 Symbol representing the independent variable. Default is 'x'. 

1068 

1069 Returns 

1070 ------- 

1071 new_series : series 

1072 Series with the specified roots. 

1073 

1074 """ 

1075 [roots] = pu.as_series([roots], trim=False) 

1076 if domain is None: 

1077 domain = pu.getdomain(roots) 

1078 elif type(domain) is list and len(domain) == 0: 

1079 domain = cls.domain 

1080 

1081 if window is None: 

1082 window = cls.window 

1083 

1084 deg = len(roots) 

1085 off, scl = pu.mapparms(domain, window) 

1086 rnew = off + scl*roots 

1087 coef = cls._fromroots(rnew) / scl**deg 

1088 return cls(coef, domain=domain, window=window, symbol=symbol) 

1089 

1090 @classmethod 

1091 def identity(cls, domain=None, window=None, symbol='x'): 

1092 """Identity function. 

1093 

1094 If ``p`` is the returned series, then ``p(x) == x`` for all 

1095 values of x. 

1096 

1097 Parameters 

1098 ---------- 

1099 domain : {None, array_like}, optional 

1100 If given, the array must be of the form ``[beg, end]``, where 

1101 ``beg`` and ``end`` are the endpoints of the domain. If None is 

1102 given then the class domain is used. The default is None. 

1103 window : {None, array_like}, optional 

1104 If given, the resulting array must be if the form 

1105 ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of 

1106 the window. If None is given then the class window is used. The 

1107 default is None. 

1108 symbol : str, optional 

1109 Symbol representing the independent variable. Default is 'x'. 

1110 

1111 Returns 

1112 ------- 

1113 new_series : series 

1114 Series of representing the identity. 

1115 

1116 """ 

1117 if domain is None: 

1118 domain = cls.domain 

1119 if window is None: 

1120 window = cls.window 

1121 off, scl = pu.mapparms(window, domain) 

1122 coef = cls._line(off, scl) 

1123 return cls(coef, domain, window, symbol) 

1124 

1125 @classmethod 

1126 def basis(cls, deg, domain=None, window=None, symbol='x'): 

1127 """Series basis polynomial of degree `deg`. 

1128 

1129 Returns the series representing the basis polynomial of degree `deg`. 

1130 

1131 .. versionadded:: 1.7.0 

1132 

1133 Parameters 

1134 ---------- 

1135 deg : int 

1136 Degree of the basis polynomial for the series. Must be >= 0. 

1137 domain : {None, array_like}, optional 

1138 If given, the array must be of the form ``[beg, end]``, where 

1139 ``beg`` and ``end`` are the endpoints of the domain. If None is 

1140 given then the class domain is used. The default is None. 

1141 window : {None, array_like}, optional 

1142 If given, the resulting array must be if the form 

1143 ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of 

1144 the window. If None is given then the class window is used. The 

1145 default is None. 

1146 symbol : str, optional 

1147 Symbol representing the independent variable. Default is 'x'. 

1148 

1149 Returns 

1150 ------- 

1151 new_series : series 

1152 A series with the coefficient of the `deg` term set to one and 

1153 all others zero. 

1154 

1155 """ 

1156 if domain is None: 

1157 domain = cls.domain 

1158 if window is None: 

1159 window = cls.window 

1160 ideg = int(deg) 

1161 

1162 if ideg != deg or ideg < 0: 

1163 raise ValueError("deg must be non-negative integer") 

1164 return cls([0]*ideg + [1], domain, window, symbol) 

1165 

1166 @classmethod 

1167 def cast(cls, series, domain=None, window=None): 

1168 """Convert series to series of this class. 

1169 

1170 The `series` is expected to be an instance of some polynomial 

1171 series of one of the types supported by by the numpy.polynomial 

1172 module, but could be some other class that supports the convert 

1173 method. 

1174 

1175 .. versionadded:: 1.7.0 

1176 

1177 Parameters 

1178 ---------- 

1179 series : series 

1180 The series instance to be converted. 

1181 domain : {None, array_like}, optional 

1182 If given, the array must be of the form ``[beg, end]``, where 

1183 ``beg`` and ``end`` are the endpoints of the domain. If None is 

1184 given then the class domain is used. The default is None. 

1185 window : {None, array_like}, optional 

1186 If given, the resulting array must be if the form 

1187 ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of 

1188 the window. If None is given then the class window is used. The 

1189 default is None. 

1190 

1191 Returns 

1192 ------- 

1193 new_series : series 

1194 A series of the same kind as the calling class and equal to 

1195 `series` when evaluated. 

1196 

1197 See Also 

1198 -------- 

1199 convert : similar instance method 

1200 

1201 """ 

1202 if domain is None: 

1203 domain = cls.domain 

1204 if window is None: 

1205 window = cls.window 

1206 return series.convert(domain, cls, window)