Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-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 """ 

686 return len(self) - 1 

687 

688 def cutdeg(self, deg): 

689 """Truncate series to the given degree. 

690 

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

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

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

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

695 small. 

696 

697 .. versionadded:: 1.5.0 

698 

699 Parameters 

700 ---------- 

701 deg : non-negative int 

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

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

704 

705 Returns 

706 ------- 

707 new_series : series 

708 New instance of series with reduced degree. 

709 

710 """ 

711 return self.truncate(deg + 1) 

712 

713 def trim(self, tol=0): 

714 """Remove trailing coefficients 

715 

716 Remove trailing coefficients until a coefficient is reached whose 

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

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

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

720 coefficients. The current instance remains unchanged. 

721 

722 Parameters 

723 ---------- 

724 tol : non-negative number. 

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

726 

727 Returns 

728 ------- 

729 new_series : series 

730 New instance of series with trimmed coefficients. 

731 

732 """ 

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

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

735 

736 def truncate(self, size): 

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

738 

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

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

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

742 high degree terms may be very small. 

743 

744 Parameters 

745 ---------- 

746 size : positive int 

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

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

749 

750 Returns 

751 ------- 

752 new_series : series 

753 New instance of series with truncated coefficients. 

754 

755 """ 

756 isize = int(size) 

757 if isize != size or isize < 1: 

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

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

760 coef = self.coef 

761 else: 

762 coef = self.coef[:isize] 

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

764 

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

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

767 

768 Parameters 

769 ---------- 

770 domain : array_like, optional 

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

772 the default domain of `kind` is used. 

773 kind : class, optional 

774 The polynomial series type class to which the current instance 

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

776 current instance is used. 

777 window : array_like, optional 

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

779 the default window of `kind` is used. 

780 

781 Returns 

782 ------- 

783 new_series : series 

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

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

786 window. 

787 

788 Notes 

789 ----- 

790 Conversion between domains and class types can result in 

791 numerically ill defined series. 

792 

793 """ 

794 if kind is None: 

795 kind = self.__class__ 

796 if domain is None: 

797 domain = kind.domain 

798 if window is None: 

799 window = kind.window 

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

801 

802 def mapparms(self): 

803 """Return the mapping parameters. 

804 

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

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

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

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

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

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

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

812 the base polynomials. 

813 

814 Returns 

815 ------- 

816 off, scl : float or complex 

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

818 

819 Notes 

820 ----- 

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

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

823 defined by the equations:: 

824 

825 L(l1) = l2 

826 L(r1) = r2 

827 

828 """ 

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

830 

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

832 """Integrate. 

833 

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

835 current series. 

836 

837 Parameters 

838 ---------- 

839 m : non-negative int 

840 The number of integrations to perform. 

841 k : array_like 

842 Integration constants. The first constant is applied to the 

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

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

845 missing values are set to zero. 

846 lbnd : Scalar 

847 The lower bound of the definite integral. 

848 

849 Returns 

850 ------- 

851 new_series : series 

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

853 as the domain of the integrated series. 

854 

855 """ 

856 off, scl = self.mapparms() 

857 if lbnd is None: 

858 lbnd = 0 

859 else: 

860 lbnd = off + scl*lbnd 

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

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

863 

864 def deriv(self, m=1): 

865 """Differentiate. 

866 

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

868 series. 

869 

870 Parameters 

871 ---------- 

872 m : non-negative int 

873 Find the derivative of order `m`. 

874 

875 Returns 

876 ------- 

877 new_series : series 

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

879 as the domain of the differentiated series. 

880 

881 """ 

882 off, scl = self.mapparms() 

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

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

885 

886 def roots(self): 

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

888 

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

890 roots decrease the further outside the domain they lie. 

891 

892 Returns 

893 ------- 

894 roots : ndarray 

895 Array containing the roots of the series. 

896 

897 """ 

898 roots = self._roots(self.coef) 

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

900 

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

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

903 

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

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

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

907 This method is intended mostly as a plotting aid. 

908 

909 .. versionadded:: 1.5.0 

910 

911 Parameters 

912 ---------- 

913 n : int, optional 

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

915 domain : {None, array_like}, optional 

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

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

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

919 

920 Returns 

921 ------- 

922 x, y : ndarray 

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

924 y is the series evaluated at element of x. 

925 

926 """ 

927 if domain is None: 

928 domain = self.domain 

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

930 y = self(x) 

931 return x, y 

932 

933 @classmethod 

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

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

936 """Least squares fit to data. 

937 

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

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

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

941 chance of ill conditioning. 

942 

943 Parameters 

944 ---------- 

945 x : array_like, shape (M,) 

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

947 y : array_like, shape (M,) 

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

949 deg : int or 1-D array_like 

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

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

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

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

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

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

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

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

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

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

960 rcond : float, optional 

961 Relative condition number of the fit. Singular values smaller 

962 than this relative to the largest singular value will be 

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

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

965 cases. 

966 full : bool, optional 

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

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

969 diagnostic information from the singular value decomposition is 

970 also returned. 

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

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

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

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

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

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

977 

978 .. versionadded:: 1.5.0 

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

980 Window to use for the returned series. The default 

981 value is the default class domain 

982 

983 .. versionadded:: 1.6.0 

984 symbol : str, optional 

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

986 

987 Returns 

988 ------- 

989 new_series : series 

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

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

992 coefficients for the unscaled and unshifted basis polynomials are 

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

994 

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

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

997 

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

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

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

1001 - rcond -- value of `rcond`. 

1002 

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

1004 

1005 """ 

1006 if domain is None: 

1007 domain = pu.getdomain(x) 

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

1009 domain = cls.domain 

1010 

1011 if window is None: 

1012 window = cls.window 

1013 

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

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

1016 if full: 

1017 [coef, status] = res 

1018 return ( 

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

1020 ) 

1021 else: 

1022 coef = res 

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

1024 

1025 @classmethod 

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

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

1028 

1029 Returns a series representing the product 

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

1031 list of roots. 

1032 

1033 Parameters 

1034 ---------- 

1035 roots : array_like 

1036 List of roots. 

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

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

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

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

1041 window : {None, array_like}, optional 

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

1043 used. The default is None. 

1044 symbol : str, optional 

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

1046 

1047 Returns 

1048 ------- 

1049 new_series : series 

1050 Series with the specified roots. 

1051 

1052 """ 

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

1054 if domain is None: 

1055 domain = pu.getdomain(roots) 

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

1057 domain = cls.domain 

1058 

1059 if window is None: 

1060 window = cls.window 

1061 

1062 deg = len(roots) 

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

1064 rnew = off + scl*roots 

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

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

1067 

1068 @classmethod 

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

1070 """Identity function. 

1071 

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

1073 values of x. 

1074 

1075 Parameters 

1076 ---------- 

1077 domain : {None, array_like}, optional 

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

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

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

1081 window : {None, array_like}, optional 

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

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

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

1085 default is None. 

1086 symbol : str, optional 

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

1088 

1089 Returns 

1090 ------- 

1091 new_series : series 

1092 Series of representing the identity. 

1093 

1094 """ 

1095 if domain is None: 

1096 domain = cls.domain 

1097 if window is None: 

1098 window = cls.window 

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

1100 coef = cls._line(off, scl) 

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

1102 

1103 @classmethod 

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

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

1106 

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

1108 

1109 .. versionadded:: 1.7.0 

1110 

1111 Parameters 

1112 ---------- 

1113 deg : int 

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

1115 domain : {None, array_like}, optional 

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

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

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

1119 window : {None, array_like}, optional 

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

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

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

1123 default is None. 

1124 symbol : str, optional 

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

1126 

1127 Returns 

1128 ------- 

1129 new_series : series 

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

1131 all others zero. 

1132 

1133 """ 

1134 if domain is None: 

1135 domain = cls.domain 

1136 if window is None: 

1137 window = cls.window 

1138 ideg = int(deg) 

1139 

1140 if ideg != deg or ideg < 0: 

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

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

1143 

1144 @classmethod 

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

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

1147 

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

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

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

1151 method. 

1152 

1153 .. versionadded:: 1.7.0 

1154 

1155 Parameters 

1156 ---------- 

1157 series : series 

1158 The series instance to be converted. 

1159 domain : {None, array_like}, optional 

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

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

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

1163 window : {None, array_like}, optional 

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

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

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

1167 default is None. 

1168 

1169 Returns 

1170 ------- 

1171 new_series : series 

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

1173 `series` when evaluated. 

1174 

1175 See Also 

1176 -------- 

1177 convert : similar instance method 

1178 

1179 """ 

1180 if domain is None: 

1181 domain = cls.domain 

1182 if window is None: 

1183 window = cls.window 

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