Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/numpy/polynomial/_polybase.py: 23%

414 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

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 

41 Attributes 

42 ---------- 

43 coef : (N,) ndarray 

44 Series coefficients in order of increasing degree. 

45 domain : (2,) ndarray 

46 Domain that is mapped to window. 

47 window : (2,) ndarray 

48 Window that domain is mapped to. 

49 

50 Class Attributes 

51 ---------------- 

52 maxpower : int 

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

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

55 domain : (2,) ndarray 

56 Default domain of the class. 

57 window : (2,) ndarray 

58 Default window of the class. 

59 

60 """ 

61 

62 # Not hashable 

63 __hash__ = None 

64 

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

66 __array_ufunc__ = None 

67 

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

69 maxpower = 100 

70 

71 # Unicode character mappings for improved __str__ 

72 _superscript_mapping = str.maketrans({ 

73 "0": "⁰", 

74 "1": "¹", 

75 "2": "²", 

76 "3": "³", 

77 "4": "⁴", 

78 "5": "⁵", 

79 "6": "⁶", 

80 "7": "⁷", 

81 "8": "⁸", 

82 "9": "⁹" 

83 }) 

84 _subscript_mapping = str.maketrans({ 

85 "0": "₀", 

86 "1": "₁", 

87 "2": "₂", 

88 "3": "₃", 

89 "4": "₄", 

90 "5": "₅", 

91 "6": "₆", 

92 "7": "₇", 

93 "8": "₈", 

94 "9": "₉" 

95 }) 

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

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

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

99 # printing on windows. 

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

101 

102 @property 

103 @abc.abstractmethod 

104 def domain(self): 

105 pass 

106 

107 @property 

108 @abc.abstractmethod 

109 def window(self): 

110 pass 

111 

112 @property 

113 @abc.abstractmethod 

114 def basis_name(self): 

115 pass 

116 

117 @staticmethod 

118 @abc.abstractmethod 

119 def _add(c1, c2): 

120 pass 

121 

122 @staticmethod 

123 @abc.abstractmethod 

124 def _sub(c1, c2): 

125 pass 

126 

127 @staticmethod 

128 @abc.abstractmethod 

129 def _mul(c1, c2): 

130 pass 

131 

132 @staticmethod 

133 @abc.abstractmethod 

134 def _div(c1, c2): 

135 pass 

136 

137 @staticmethod 

138 @abc.abstractmethod 

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

140 pass 

141 

142 @staticmethod 

143 @abc.abstractmethod 

144 def _val(x, c): 

145 pass 

146 

147 @staticmethod 

148 @abc.abstractmethod 

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

150 pass 

151 

152 @staticmethod 

153 @abc.abstractmethod 

154 def _der(c, m, scl): 

155 pass 

156 

157 @staticmethod 

158 @abc.abstractmethod 

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

160 pass 

161 

162 @staticmethod 

163 @abc.abstractmethod 

164 def _line(off, scl): 

165 pass 

166 

167 @staticmethod 

168 @abc.abstractmethod 

169 def _roots(c): 

170 pass 

171 

172 @staticmethod 

173 @abc.abstractmethod 

174 def _fromroots(r): 

175 pass 

176 

177 def has_samecoef(self, other): 

178 """Check if coefficients match. 

179 

180 .. versionadded:: 1.6.0 

181 

182 Parameters 

183 ---------- 

184 other : class instance 

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

186 

187 Returns 

188 ------- 

189 bool : boolean 

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

191 

192 """ 

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

194 return False 

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

196 return False 

197 else: 

198 return True 

199 

200 def has_samedomain(self, other): 

201 """Check if domains match. 

202 

203 .. versionadded:: 1.6.0 

204 

205 Parameters 

206 ---------- 

207 other : class instance 

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

209 

210 Returns 

211 ------- 

212 bool : boolean 

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

214 

215 """ 

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

217 

218 def has_samewindow(self, other): 

219 """Check if windows match. 

220 

221 .. versionadded:: 1.6.0 

222 

223 Parameters 

224 ---------- 

225 other : class instance 

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

227 

228 Returns 

229 ------- 

230 bool : boolean 

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

232 

233 """ 

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

235 

236 def has_sametype(self, other): 

237 """Check if types match. 

238 

239 .. versionadded:: 1.7.0 

240 

241 Parameters 

242 ---------- 

243 other : object 

244 Class instance. 

245 

246 Returns 

247 ------- 

248 bool : boolean 

249 True if other is same class as self 

250 

251 """ 

252 return isinstance(other, self.__class__) 

253 

254 def _get_coefficients(self, other): 

255 """Interpret other as polynomial coefficients. 

256 

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

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

259 return its coefficients, otherwise return `other`. 

260 

261 .. versionadded:: 1.9.0 

262 

263 Parameters 

264 ---------- 

265 other : anything 

266 Object to be checked. 

267 

268 Returns 

269 ------- 

270 coef 

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

272 of ABCPolyBase, otherwise `other`. 

273 

274 Raises 

275 ------ 

276 TypeError 

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

278 

279 """ 

280 if isinstance(other, ABCPolyBase): 

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

282 raise TypeError("Polynomial types differ") 

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

284 raise TypeError("Domains differ") 

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

286 raise TypeError("Windows differ") 

287 return other.coef 

288 return other 

289 

290 def __init__(self, coef, domain=None, window=None): 

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

292 self.coef = coef 

293 

294 if domain is not None: 

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

296 if len(domain) != 2: 

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

298 self.domain = domain 

299 

300 if window is not None: 

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

302 if len(window) != 2: 

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

304 self.window = window 

305 

306 def __repr__(self): 

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

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

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

310 name = self.__class__.__name__ 

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

312 

313 def __format__(self, fmt_str): 

314 if fmt_str == '': 

315 return self.__str__() 

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

317 raise ValueError( 

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

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

320 f"'ascii' and 'unicode'" 

321 ) 

322 if fmt_str == 'ascii': 

323 return self._generate_string(self._str_term_ascii) 

324 return self._generate_string(self._str_term_unicode) 

325 

326 def __str__(self): 

327 if self._use_unicode: 

328 return self._generate_string(self._str_term_unicode) 

329 return self._generate_string(self._str_term_ascii) 

330 

331 def _generate_string(self, term_method): 

332 """ 

333 Generate the full string representation of the polynomial, using 

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

335 """ 

336 # Get configuration for line breaks 

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

338 if linewidth < 1: 

339 linewidth = 1 

340 out = f"{self.coef[0]}" 

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

342 out += " " 

343 power = str(i + 1) 

344 # Polynomial coefficient 

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

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

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

348 try: 

349 if coef >= 0: 

350 next_term = f"+ {coef}" 

351 else: 

352 next_term = f"- {-coef}" 

353 except TypeError: 

354 next_term = f"+ {coef}" 

355 # Polynomial term 

356 next_term += term_method(power, "x") 

357 # Length of the current line with next term added 

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

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

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

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

362 line_len += 2 

363 # Handle linebreaking 

364 if line_len >= linewidth: 

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

366 out += next_term 

367 return out 

368 

369 @classmethod 

370 def _str_term_unicode(cls, i, arg_str): 

371 """ 

372 String representation of single polynomial term using unicode 

373 characters for superscripts and subscripts. 

374 """ 

375 if cls.basis_name is None: 

376 raise NotImplementedError( 

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

378 "_str_term_unicode(cls, i, arg_str)" 

379 ) 

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

381 f"({arg_str})") 

382 

383 @classmethod 

384 def _str_term_ascii(cls, i, arg_str): 

385 """ 

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

387 represent superscripts and subscripts, respectively. 

388 """ 

389 if cls.basis_name is None: 

390 raise NotImplementedError( 

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

392 "_str_term_ascii(cls, i, arg_str)" 

393 ) 

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

395 

396 @classmethod 

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

398 if cls.basis_name is None: 

399 raise NotImplementedError( 

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

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

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

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

404 

405 @staticmethod 

406 def _repr_latex_scalar(x): 

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

408 # exponents in this function 

409 return r'\text{{{}}}'.format(x) 

410 

411 def _repr_latex_(self): 

412 # get the scaled argument string to the basis functions 

413 off, scale = self.mapparms() 

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

415 term = 'x' 

416 needs_parens = False 

417 elif scale == 1: 

418 term = f"{self._repr_latex_scalar(off)} + x" 

419 needs_parens = True 

420 elif off == 0: 

421 term = f"{self._repr_latex_scalar(scale)}x" 

422 needs_parens = True 

423 else: 

424 term = ( 

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

426 f"{self._repr_latex_scalar(scale)}x" 

427 ) 

428 needs_parens = True 

429 

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

431 

432 parts = [] 

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

434 # prevent duplication of + and - signs 

435 if i == 0: 

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

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

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

439 elif not np.signbit(c): 

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

441 else: 

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

443 

444 # produce the string for the term 

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

446 if term_str == '1': 

447 part = coef_str 

448 else: 

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

450 

451 if c == 0: 

452 part = mute(part) 

453 

454 parts.append(part) 

455 

456 if parts: 

457 body = ''.join(parts) 

458 else: 

459 # in case somehow there are no coefficients at all 

460 body = '0' 

461 

462 return rf"$x \mapsto {body}$" 

463 

464 

465 

466 # Pickle and copy 

467 

468 def __getstate__(self): 

469 ret = self.__dict__.copy() 

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

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

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

473 return ret 

474 

475 def __setstate__(self, dict): 

476 self.__dict__ = dict 

477 

478 # Call 

479 

480 def __call__(self, arg): 

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

482 arg = off + scl*arg 

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

484 

485 def __iter__(self): 

486 return iter(self.coef) 

487 

488 def __len__(self): 

489 return len(self.coef) 

490 

491 # Numeric properties. 

492 

493 def __neg__(self): 

494 return self.__class__(-self.coef, self.domain, self.window) 

495 

496 def __pos__(self): 

497 return self 

498 

499 def __add__(self, other): 

500 othercoef = self._get_coefficients(other) 

501 try: 

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

503 except Exception: 

504 return NotImplemented 

505 return self.__class__(coef, self.domain, self.window) 

506 

507 def __sub__(self, other): 

508 othercoef = self._get_coefficients(other) 

509 try: 

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

511 except Exception: 

512 return NotImplemented 

513 return self.__class__(coef, self.domain, self.window) 

514 

515 def __mul__(self, other): 

516 othercoef = self._get_coefficients(other) 

517 try: 

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

519 except Exception: 

520 return NotImplemented 

521 return self.__class__(coef, self.domain, self.window) 

522 

523 def __truediv__(self, other): 

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

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

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

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

528 raise TypeError( 

529 f"unsupported types for true division: " 

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

531 ) 

532 return self.__floordiv__(other) 

533 

534 def __floordiv__(self, other): 

535 res = self.__divmod__(other) 

536 if res is NotImplemented: 

537 return res 

538 return res[0] 

539 

540 def __mod__(self, other): 

541 res = self.__divmod__(other) 

542 if res is NotImplemented: 

543 return res 

544 return res[1] 

545 

546 def __divmod__(self, other): 

547 othercoef = self._get_coefficients(other) 

548 try: 

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

550 except ZeroDivisionError: 

551 raise 

552 except Exception: 

553 return NotImplemented 

554 quo = self.__class__(quo, self.domain, self.window) 

555 rem = self.__class__(rem, self.domain, self.window) 

556 return quo, rem 

557 

558 def __pow__(self, other): 

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

560 res = self.__class__(coef, self.domain, self.window) 

561 return res 

562 

563 def __radd__(self, other): 

564 try: 

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

566 except Exception: 

567 return NotImplemented 

568 return self.__class__(coef, self.domain, self.window) 

569 

570 def __rsub__(self, other): 

571 try: 

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

573 except Exception: 

574 return NotImplemented 

575 return self.__class__(coef, self.domain, self.window) 

576 

577 def __rmul__(self, other): 

578 try: 

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

580 except Exception: 

581 return NotImplemented 

582 return self.__class__(coef, self.domain, self.window) 

583 

584 def __rdiv__(self, other): 

585 # set to __floordiv__ /. 

586 return self.__rfloordiv__(other) 

587 

588 def __rtruediv__(self, other): 

589 # An instance of ABCPolyBase is not considered a 

590 # Number. 

591 return NotImplemented 

592 

593 def __rfloordiv__(self, other): 

594 res = self.__rdivmod__(other) 

595 if res is NotImplemented: 

596 return res 

597 return res[0] 

598 

599 def __rmod__(self, other): 

600 res = self.__rdivmod__(other) 

601 if res is NotImplemented: 

602 return res 

603 return res[1] 

604 

605 def __rdivmod__(self, other): 

606 try: 

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

608 except ZeroDivisionError: 

609 raise 

610 except Exception: 

611 return NotImplemented 

612 quo = self.__class__(quo, self.domain, self.window) 

613 rem = self.__class__(rem, self.domain, self.window) 

614 return quo, rem 

615 

616 def __eq__(self, other): 

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

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

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

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

621 np.all(self.coef == other.coef)) 

622 return res 

623 

624 def __ne__(self, other): 

625 return not self.__eq__(other) 

626 

627 # 

628 # Extra methods. 

629 # 

630 

631 def copy(self): 

632 """Return a copy. 

633 

634 Returns 

635 ------- 

636 new_series : series 

637 Copy of self. 

638 

639 """ 

640 return self.__class__(self.coef, self.domain, self.window) 

641 

642 def degree(self): 

643 """The degree of the series. 

644 

645 .. versionadded:: 1.5.0 

646 

647 Returns 

648 ------- 

649 degree : int 

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

651 

652 """ 

653 return len(self) - 1 

654 

655 def cutdeg(self, deg): 

656 """Truncate series to the given degree. 

657 

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

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

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

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

662 small. 

663 

664 .. versionadded:: 1.5.0 

665 

666 Parameters 

667 ---------- 

668 deg : non-negative int 

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

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

671 

672 Returns 

673 ------- 

674 new_series : series 

675 New instance of series with reduced degree. 

676 

677 """ 

678 return self.truncate(deg + 1) 

679 

680 def trim(self, tol=0): 

681 """Remove trailing coefficients 

682 

683 Remove trailing coefficients until a coefficient is reached whose 

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

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

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

687 coefficients. The current instance remains unchanged. 

688 

689 Parameters 

690 ---------- 

691 tol : non-negative number. 

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

693 

694 Returns 

695 ------- 

696 new_series : series 

697 New instance of series with trimmed coefficients. 

698 

699 """ 

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

701 return self.__class__(coef, self.domain, self.window) 

702 

703 def truncate(self, size): 

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

705 

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

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

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

709 high degree terms may be very small. 

710 

711 Parameters 

712 ---------- 

713 size : positive int 

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

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

716 

717 Returns 

718 ------- 

719 new_series : series 

720 New instance of series with truncated coefficients. 

721 

722 """ 

723 isize = int(size) 

724 if isize != size or isize < 1: 

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

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

727 coef = self.coef 

728 else: 

729 coef = self.coef[:isize] 

730 return self.__class__(coef, self.domain, self.window) 

731 

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

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

734 

735 Parameters 

736 ---------- 

737 domain : array_like, optional 

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

739 the default domain of `kind` is used. 

740 kind : class, optional 

741 The polynomial series type class to which the current instance 

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

743 current instance is used. 

744 window : array_like, optional 

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

746 the default window of `kind` is used. 

747 

748 Returns 

749 ------- 

750 new_series : series 

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

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

753 window. 

754 

755 Notes 

756 ----- 

757 Conversion between domains and class types can result in 

758 numerically ill defined series. 

759 

760 """ 

761 if kind is None: 

762 kind = self.__class__ 

763 if domain is None: 

764 domain = kind.domain 

765 if window is None: 

766 window = kind.window 

767 return self(kind.identity(domain, window=window)) 

768 

769 def mapparms(self): 

770 """Return the mapping parameters. 

771 

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

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

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

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

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

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

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

779 the base polynomials. 

780 

781 Returns 

782 ------- 

783 off, scl : float or complex 

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

785 

786 Notes 

787 ----- 

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

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

790 defined by the equations:: 

791 

792 L(l1) = l2 

793 L(r1) = r2 

794 

795 """ 

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

797 

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

799 """Integrate. 

800 

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

802 current series. 

803 

804 Parameters 

805 ---------- 

806 m : non-negative int 

807 The number of integrations to perform. 

808 k : array_like 

809 Integration constants. The first constant is applied to the 

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

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

812 missing values are set to zero. 

813 lbnd : Scalar 

814 The lower bound of the definite integral. 

815 

816 Returns 

817 ------- 

818 new_series : series 

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

820 as the domain of the integrated series. 

821 

822 """ 

823 off, scl = self.mapparms() 

824 if lbnd is None: 

825 lbnd = 0 

826 else: 

827 lbnd = off + scl*lbnd 

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

829 return self.__class__(coef, self.domain, self.window) 

830 

831 def deriv(self, m=1): 

832 """Differentiate. 

833 

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

835 series. 

836 

837 Parameters 

838 ---------- 

839 m : non-negative int 

840 Find the derivative of order `m`. 

841 

842 Returns 

843 ------- 

844 new_series : series 

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

846 as the domain of the differentiated series. 

847 

848 """ 

849 off, scl = self.mapparms() 

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

851 return self.__class__(coef, self.domain, self.window) 

852 

853 def roots(self): 

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

855 

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

857 roots decrease the further outside the domain they lie. 

858 

859 Returns 

860 ------- 

861 roots : ndarray 

862 Array containing the roots of the series. 

863 

864 """ 

865 roots = self._roots(self.coef) 

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

867 

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

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

870 

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

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

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

874 This method is intended mostly as a plotting aid. 

875 

876 .. versionadded:: 1.5.0 

877 

878 Parameters 

879 ---------- 

880 n : int, optional 

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

882 domain : {None, array_like}, optional 

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

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

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

886 

887 Returns 

888 ------- 

889 x, y : ndarray 

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

891 y is the series evaluated at element of x. 

892 

893 """ 

894 if domain is None: 

895 domain = self.domain 

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

897 y = self(x) 

898 return x, y 

899 

900 @classmethod 

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

902 window=None): 

903 """Least squares fit to data. 

904 

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

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

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

908 chance of ill conditioning. 

909 

910 Parameters 

911 ---------- 

912 x : array_like, shape (M,) 

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

914 y : array_like, shape (M,) 

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

916 deg : int or 1-D array_like 

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

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

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

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

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

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

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

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

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

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

927 rcond : float, optional 

928 Relative condition number of the fit. Singular values smaller 

929 than this relative to the largest singular value will be 

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

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

932 cases. 

933 full : bool, optional 

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

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

936 diagnostic information from the singular value decomposition is 

937 also returned. 

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

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

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

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

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

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

944 

945 .. versionadded:: 1.5.0 

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

947 Window to use for the returned series. The default 

948 value is the default class domain 

949 

950 .. versionadded:: 1.6.0 

951 

952 Returns 

953 ------- 

954 new_series : series 

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

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

957 coefficients for the unscaled and unshifted basis polynomials are 

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

959 

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

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

962 

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

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

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

966 - rcond -- value of `rcond`. 

967 

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

969 

970 """ 

971 if domain is None: 

972 domain = pu.getdomain(x) 

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

974 domain = cls.domain 

975 

976 if window is None: 

977 window = cls.window 

978 

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

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

981 if full: 

982 [coef, status] = res 

983 return cls(coef, domain=domain, window=window), status 

984 else: 

985 coef = res 

986 return cls(coef, domain=domain, window=window) 

987 

988 @classmethod 

989 def fromroots(cls, roots, domain=[], window=None): 

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

991 

992 Returns a series representing the product 

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

994 list of roots. 

995 

996 Parameters 

997 ---------- 

998 roots : array_like 

999 List of roots. 

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

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

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

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

1004 window : {None, array_like}, optional 

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

1006 used. The default is None. 

1007 

1008 Returns 

1009 ------- 

1010 new_series : series 

1011 Series with the specified roots. 

1012 

1013 """ 

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

1015 if domain is None: 

1016 domain = pu.getdomain(roots) 

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

1018 domain = cls.domain 

1019 

1020 if window is None: 

1021 window = cls.window 

1022 

1023 deg = len(roots) 

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

1025 rnew = off + scl*roots 

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

1027 return cls(coef, domain=domain, window=window) 

1028 

1029 @classmethod 

1030 def identity(cls, domain=None, window=None): 

1031 """Identity function. 

1032 

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

1034 values of x. 

1035 

1036 Parameters 

1037 ---------- 

1038 domain : {None, array_like}, optional 

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

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

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

1042 window : {None, array_like}, optional 

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

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

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

1046 default is None. 

1047 

1048 Returns 

1049 ------- 

1050 new_series : series 

1051 Series of representing the identity. 

1052 

1053 """ 

1054 if domain is None: 

1055 domain = cls.domain 

1056 if window is None: 

1057 window = cls.window 

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

1059 coef = cls._line(off, scl) 

1060 return cls(coef, domain, window) 

1061 

1062 @classmethod 

1063 def basis(cls, deg, domain=None, window=None): 

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

1065 

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

1067 

1068 .. versionadded:: 1.7.0 

1069 

1070 Parameters 

1071 ---------- 

1072 deg : int 

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

1074 domain : {None, array_like}, optional 

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

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

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

1078 window : {None, array_like}, optional 

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

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

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

1082 default is None. 

1083 

1084 Returns 

1085 ------- 

1086 new_series : series 

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

1088 all others zero. 

1089 

1090 """ 

1091 if domain is None: 

1092 domain = cls.domain 

1093 if window is None: 

1094 window = cls.window 

1095 ideg = int(deg) 

1096 

1097 if ideg != deg or ideg < 0: 

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

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

1100 

1101 @classmethod 

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

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

1104 

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

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

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

1108 method. 

1109 

1110 .. versionadded:: 1.7.0 

1111 

1112 Parameters 

1113 ---------- 

1114 series : series 

1115 The series instance to be converted. 

1116 domain : {None, array_like}, optional 

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

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

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

1120 window : {None, array_like}, optional 

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

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

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

1124 default is None. 

1125 

1126 Returns 

1127 ------- 

1128 new_series : series 

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

1130 `series` when evaluated. 

1131 

1132 See Also 

1133 -------- 

1134 convert : similar instance method 

1135 

1136 """ 

1137 if domain is None: 

1138 domain = cls.domain 

1139 if window is None: 

1140 window = cls.window 

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