Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/numpy/polynomial/polyutils.py: 11%

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

253 statements  

1""" 

2Utility classes and functions for the polynomial modules. 

3 

4This module provides: error and warning objects; a polynomial base class; 

5and some routines used in both the `polynomial` and `chebyshev` modules. 

6 

7Warning objects 

8--------------- 

9 

10.. autosummary:: 

11 :toctree: generated/ 

12 

13 RankWarning raised in least-squares fit for rank-deficient matrix. 

14 

15Functions 

16--------- 

17 

18.. autosummary:: 

19 :toctree: generated/ 

20 

21 as_series convert list of array_likes into 1-D arrays of common type. 

22 trimseq remove trailing zeros. 

23 trimcoef remove small trailing coefficients. 

24 getdomain return the domain appropriate for a given set of abscissae. 

25 mapdomain maps points between domains. 

26 mapparms parameters of the linear map between domains. 

27 

28""" 

29import operator 

30import functools 

31import warnings 

32 

33import numpy as np 

34 

35from numpy.core.multiarray import dragon4_positional, dragon4_scientific 

36from numpy.core.umath import absolute 

37 

38__all__ = [ 

39 'RankWarning', 'as_series', 'trimseq', 

40 'trimcoef', 'getdomain', 'mapdomain', 'mapparms', 

41 'format_float'] 

42 

43# 

44# Warnings and Exceptions 

45# 

46 

47class RankWarning(UserWarning): 

48 """Issued by chebfit when the design matrix is rank deficient.""" 

49 pass 

50 

51# 

52# Helper functions to convert inputs to 1-D arrays 

53# 

54def trimseq(seq): 

55 """Remove small Poly series coefficients. 

56 

57 Parameters 

58 ---------- 

59 seq : sequence 

60 Sequence of Poly series coefficients. This routine fails for 

61 empty sequences. 

62 

63 Returns 

64 ------- 

65 series : sequence 

66 Subsequence with trailing zeros removed. If the resulting sequence 

67 would be empty, return the first element. The returned sequence may 

68 or may not be a view. 

69 

70 Notes 

71 ----- 

72 Do not lose the type info if the sequence contains unknown objects. 

73 

74 """ 

75 if len(seq) == 0: 

76 return seq 

77 else: 

78 for i in range(len(seq) - 1, -1, -1): 

79 if seq[i] != 0: 

80 break 

81 return seq[:i+1] 

82 

83 

84def as_series(alist, trim=True): 

85 """ 

86 Return argument as a list of 1-d arrays. 

87 

88 The returned list contains array(s) of dtype double, complex double, or 

89 object. A 1-d argument of shape ``(N,)`` is parsed into ``N`` arrays of 

90 size one; a 2-d argument of shape ``(M,N)`` is parsed into ``M`` arrays 

91 of size ``N`` (i.e., is "parsed by row"); and a higher dimensional array 

92 raises a Value Error if it is not first reshaped into either a 1-d or 2-d 

93 array. 

94 

95 Parameters 

96 ---------- 

97 alist : array_like 

98 A 1- or 2-d array_like 

99 trim : boolean, optional 

100 When True, trailing zeros are removed from the inputs. 

101 When False, the inputs are passed through intact. 

102 

103 Returns 

104 ------- 

105 [a1, a2,...] : list of 1-D arrays 

106 A copy of the input data as a list of 1-d arrays. 

107 

108 Raises 

109 ------ 

110 ValueError 

111 Raised when `as_series` cannot convert its input to 1-d arrays, or at 

112 least one of the resulting arrays is empty. 

113 

114 Examples 

115 -------- 

116 >>> from numpy.polynomial import polyutils as pu 

117 >>> a = np.arange(4) 

118 >>> pu.as_series(a) 

119 [array([0.]), array([1.]), array([2.]), array([3.])] 

120 >>> b = np.arange(6).reshape((2,3)) 

121 >>> pu.as_series(b) 

122 [array([0., 1., 2.]), array([3., 4., 5.])] 

123 

124 >>> pu.as_series((1, np.arange(3), np.arange(2, dtype=np.float16))) 

125 [array([1.]), array([0., 1., 2.]), array([0., 1.])] 

126 

127 >>> pu.as_series([2, [1.1, 0.]]) 

128 [array([2.]), array([1.1])] 

129 

130 >>> pu.as_series([2, [1.1, 0.]], trim=False) 

131 [array([2.]), array([1.1, 0. ])] 

132 

133 """ 

134 arrays = [np.array(a, ndmin=1, copy=False) for a in alist] 

135 if min([a.size for a in arrays]) == 0: 

136 raise ValueError("Coefficient array is empty") 

137 if any(a.ndim != 1 for a in arrays): 

138 raise ValueError("Coefficient array is not 1-d") 

139 if trim: 

140 arrays = [trimseq(a) for a in arrays] 

141 

142 if any(a.dtype == np.dtype(object) for a in arrays): 

143 ret = [] 

144 for a in arrays: 

145 if a.dtype != np.dtype(object): 

146 tmp = np.empty(len(a), dtype=np.dtype(object)) 

147 tmp[:] = a[:] 

148 ret.append(tmp) 

149 else: 

150 ret.append(a.copy()) 

151 else: 

152 try: 

153 dtype = np.common_type(*arrays) 

154 except Exception as e: 

155 raise ValueError("Coefficient arrays have no common type") from e 

156 ret = [np.array(a, copy=True, dtype=dtype) for a in arrays] 

157 return ret 

158 

159 

160def trimcoef(c, tol=0): 

161 """ 

162 Remove "small" "trailing" coefficients from a polynomial. 

163 

164 "Small" means "small in absolute value" and is controlled by the 

165 parameter `tol`; "trailing" means highest order coefficient(s), e.g., in 

166 ``[0, 1, 1, 0, 0]`` (which represents ``0 + x + x**2 + 0*x**3 + 0*x**4``) 

167 both the 3-rd and 4-th order coefficients would be "trimmed." 

168 

169 Parameters 

170 ---------- 

171 c : array_like 

172 1-d array of coefficients, ordered from lowest order to highest. 

173 tol : number, optional 

174 Trailing (i.e., highest order) elements with absolute value less 

175 than or equal to `tol` (default value is zero) are removed. 

176 

177 Returns 

178 ------- 

179 trimmed : ndarray 

180 1-d array with trailing zeros removed. If the resulting series 

181 would be empty, a series containing a single zero is returned. 

182 

183 Raises 

184 ------ 

185 ValueError 

186 If `tol` < 0 

187 

188 See Also 

189 -------- 

190 trimseq 

191 

192 Examples 

193 -------- 

194 >>> from numpy.polynomial import polyutils as pu 

195 >>> pu.trimcoef((0,0,3,0,5,0,0)) 

196 array([0., 0., 3., 0., 5.]) 

197 >>> pu.trimcoef((0,0,1e-3,0,1e-5,0,0),1e-3) # item == tol is trimmed 

198 array([0.]) 

199 >>> i = complex(0,1) # works for complex 

200 >>> pu.trimcoef((3e-4,1e-3*(1-i),5e-4,2e-5*(1+i)), 1e-3) 

201 array([0.0003+0.j , 0.001 -0.001j]) 

202 

203 """ 

204 if tol < 0: 

205 raise ValueError("tol must be non-negative") 

206 

207 [c] = as_series([c]) 

208 [ind] = np.nonzero(np.abs(c) > tol) 

209 if len(ind) == 0: 

210 return c[:1]*0 

211 else: 

212 return c[:ind[-1] + 1].copy() 

213 

214def getdomain(x): 

215 """ 

216 Return a domain suitable for given abscissae. 

217 

218 Find a domain suitable for a polynomial or Chebyshev series 

219 defined at the values supplied. 

220 

221 Parameters 

222 ---------- 

223 x : array_like 

224 1-d array of abscissae whose domain will be determined. 

225 

226 Returns 

227 ------- 

228 domain : ndarray 

229 1-d array containing two values. If the inputs are complex, then 

230 the two returned points are the lower left and upper right corners 

231 of the smallest rectangle (aligned with the axes) in the complex 

232 plane containing the points `x`. If the inputs are real, then the 

233 two points are the ends of the smallest interval containing the 

234 points `x`. 

235 

236 See Also 

237 -------- 

238 mapparms, mapdomain 

239 

240 Examples 

241 -------- 

242 >>> from numpy.polynomial import polyutils as pu 

243 >>> points = np.arange(4)**2 - 5; points 

244 array([-5, -4, -1, 4]) 

245 >>> pu.getdomain(points) 

246 array([-5., 4.]) 

247 >>> c = np.exp(complex(0,1)*np.pi*np.arange(12)/6) # unit circle 

248 >>> pu.getdomain(c) 

249 array([-1.-1.j, 1.+1.j]) 

250 

251 """ 

252 [x] = as_series([x], trim=False) 

253 if x.dtype.char in np.typecodes['Complex']: 

254 rmin, rmax = x.real.min(), x.real.max() 

255 imin, imax = x.imag.min(), x.imag.max() 

256 return np.array((complex(rmin, imin), complex(rmax, imax))) 

257 else: 

258 return np.array((x.min(), x.max())) 

259 

260def mapparms(old, new): 

261 """ 

262 Linear map parameters between domains. 

263 

264 Return the parameters of the linear map ``offset + scale*x`` that maps 

265 `old` to `new` such that ``old[i] -> new[i]``, ``i = 0, 1``. 

266 

267 Parameters 

268 ---------- 

269 old, new : array_like 

270 Domains. Each domain must (successfully) convert to a 1-d array 

271 containing precisely two values. 

272 

273 Returns 

274 ------- 

275 offset, scale : scalars 

276 The map ``L(x) = offset + scale*x`` maps the first domain to the 

277 second. 

278 

279 See Also 

280 -------- 

281 getdomain, mapdomain 

282 

283 Notes 

284 ----- 

285 Also works for complex numbers, and thus can be used to calculate the 

286 parameters required to map any line in the complex plane to any other 

287 line therein. 

288 

289 Examples 

290 -------- 

291 >>> from numpy.polynomial import polyutils as pu 

292 >>> pu.mapparms((-1,1),(-1,1)) 

293 (0.0, 1.0) 

294 >>> pu.mapparms((1,-1),(-1,1)) 

295 (-0.0, -1.0) 

296 >>> i = complex(0,1) 

297 >>> pu.mapparms((-i,-1),(1,i)) 

298 ((1+1j), (1-0j)) 

299 

300 """ 

301 oldlen = old[1] - old[0] 

302 newlen = new[1] - new[0] 

303 off = (old[1]*new[0] - old[0]*new[1])/oldlen 

304 scl = newlen/oldlen 

305 return off, scl 

306 

307def mapdomain(x, old, new): 

308 """ 

309 Apply linear map to input points. 

310 

311 The linear map ``offset + scale*x`` that maps the domain `old` to 

312 the domain `new` is applied to the points `x`. 

313 

314 Parameters 

315 ---------- 

316 x : array_like 

317 Points to be mapped. If `x` is a subtype of ndarray the subtype 

318 will be preserved. 

319 old, new : array_like 

320 The two domains that determine the map. Each must (successfully) 

321 convert to 1-d arrays containing precisely two values. 

322 

323 Returns 

324 ------- 

325 x_out : ndarray 

326 Array of points of the same shape as `x`, after application of the 

327 linear map between the two domains. 

328 

329 See Also 

330 -------- 

331 getdomain, mapparms 

332 

333 Notes 

334 ----- 

335 Effectively, this implements: 

336 

337 .. math:: 

338 x\\_out = new[0] + m(x - old[0]) 

339 

340 where 

341 

342 .. math:: 

343 m = \\frac{new[1]-new[0]}{old[1]-old[0]} 

344 

345 Examples 

346 -------- 

347 >>> from numpy.polynomial import polyutils as pu 

348 >>> old_domain = (-1,1) 

349 >>> new_domain = (0,2*np.pi) 

350 >>> x = np.linspace(-1,1,6); x 

351 array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ]) 

352 >>> x_out = pu.mapdomain(x, old_domain, new_domain); x_out 

353 array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825, # may vary 

354 6.28318531]) 

355 >>> x - pu.mapdomain(x_out, new_domain, old_domain) 

356 array([0., 0., 0., 0., 0., 0.]) 

357 

358 Also works for complex numbers (and thus can be used to map any line in 

359 the complex plane to any other line therein). 

360 

361 >>> i = complex(0,1) 

362 >>> old = (-1 - i, 1 + i) 

363 >>> new = (-1 + i, 1 - i) 

364 >>> z = np.linspace(old[0], old[1], 6); z 

365 array([-1. -1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1. +1.j ]) 

366 >>> new_z = pu.mapdomain(z, old, new); new_z 

367 array([-1.0+1.j , -0.6+0.6j, -0.2+0.2j, 0.2-0.2j, 0.6-0.6j, 1.0-1.j ]) # may vary 

368 

369 """ 

370 x = np.asanyarray(x) 

371 off, scl = mapparms(old, new) 

372 return off + scl*x 

373 

374 

375def _nth_slice(i, ndim): 

376 sl = [np.newaxis] * ndim 

377 sl[i] = slice(None) 

378 return tuple(sl) 

379 

380 

381def _vander_nd(vander_fs, points, degrees): 

382 r""" 

383 A generalization of the Vandermonde matrix for N dimensions 

384 

385 The result is built by combining the results of 1d Vandermonde matrices, 

386 

387 .. math:: 

388 W[i_0, \ldots, i_M, j_0, \ldots, j_N] = \prod_{k=0}^N{V_k(x_k)[i_0, \ldots, i_M, j_k]} 

389 

390 where 

391 

392 .. math:: 

393 N &= \texttt{len(points)} = \texttt{len(degrees)} = \texttt{len(vander\_fs)} \\ 

394 M &= \texttt{points[k].ndim} \\ 

395 V_k &= \texttt{vander\_fs[k]} \\ 

396 x_k &= \texttt{points[k]} \\ 

397 0 \le j_k &\le \texttt{degrees[k]} 

398 

399 Expanding the one-dimensional :math:`V_k` functions gives: 

400 

401 .. math:: 

402 W[i_0, \ldots, i_M, j_0, \ldots, j_N] = \prod_{k=0}^N{B_{k, j_k}(x_k[i_0, \ldots, i_M])} 

403 

404 where :math:`B_{k,m}` is the m'th basis of the polynomial construction used along 

405 dimension :math:`k`. For a regular polynomial, :math:`B_{k, m}(x) = P_m(x) = x^m`. 

406 

407 Parameters 

408 ---------- 

409 vander_fs : Sequence[function(array_like, int) -> ndarray] 

410 The 1d vander function to use for each axis, such as ``polyvander`` 

411 points : Sequence[array_like] 

412 Arrays of point coordinates, all of the same shape. The dtypes 

413 will be converted to either float64 or complex128 depending on 

414 whether any of the elements are complex. Scalars are converted to 

415 1-D arrays. 

416 This must be the same length as `vander_fs`. 

417 degrees : Sequence[int] 

418 The maximum degree (inclusive) to use for each axis. 

419 This must be the same length as `vander_fs`. 

420 

421 Returns 

422 ------- 

423 vander_nd : ndarray 

424 An array of shape ``points[0].shape + tuple(d + 1 for d in degrees)``. 

425 """ 

426 n_dims = len(vander_fs) 

427 if n_dims != len(points): 

428 raise ValueError( 

429 f"Expected {n_dims} dimensions of sample points, got {len(points)}") 

430 if n_dims != len(degrees): 

431 raise ValueError( 

432 f"Expected {n_dims} dimensions of degrees, got {len(degrees)}") 

433 if n_dims == 0: 

434 raise ValueError("Unable to guess a dtype or shape when no points are given") 

435 

436 # convert to the same shape and type 

437 points = tuple(np.array(tuple(points), copy=False) + 0.0) 

438 

439 # produce the vandermonde matrix for each dimension, placing the last 

440 # axis of each in an independent trailing axis of the output 

441 vander_arrays = ( 

442 vander_fs[i](points[i], degrees[i])[(...,) + _nth_slice(i, n_dims)] 

443 for i in range(n_dims) 

444 ) 

445 

446 # we checked this wasn't empty already, so no `initial` needed 

447 return functools.reduce(operator.mul, vander_arrays) 

448 

449 

450def _vander_nd_flat(vander_fs, points, degrees): 

451 """ 

452 Like `_vander_nd`, but flattens the last ``len(degrees)`` axes into a single axis 

453 

454 Used to implement the public ``<type>vander<n>d`` functions. 

455 """ 

456 v = _vander_nd(vander_fs, points, degrees) 

457 return v.reshape(v.shape[:-len(degrees)] + (-1,)) 

458 

459 

460def _fromroots(line_f, mul_f, roots): 

461 """ 

462 Helper function used to implement the ``<type>fromroots`` functions. 

463 

464 Parameters 

465 ---------- 

466 line_f : function(float, float) -> ndarray 

467 The ``<type>line`` function, such as ``polyline`` 

468 mul_f : function(array_like, array_like) -> ndarray 

469 The ``<type>mul`` function, such as ``polymul`` 

470 roots 

471 See the ``<type>fromroots`` functions for more detail 

472 """ 

473 if len(roots) == 0: 

474 return np.ones(1) 

475 else: 

476 [roots] = as_series([roots], trim=False) 

477 roots.sort() 

478 p = [line_f(-r, 1) for r in roots] 

479 n = len(p) 

480 while n > 1: 

481 m, r = divmod(n, 2) 

482 tmp = [mul_f(p[i], p[i+m]) for i in range(m)] 

483 if r: 

484 tmp[0] = mul_f(tmp[0], p[-1]) 

485 p = tmp 

486 n = m 

487 return p[0] 

488 

489 

490def _valnd(val_f, c, *args): 

491 """ 

492 Helper function used to implement the ``<type>val<n>d`` functions. 

493 

494 Parameters 

495 ---------- 

496 val_f : function(array_like, array_like, tensor: bool) -> array_like 

497 The ``<type>val`` function, such as ``polyval`` 

498 c, args 

499 See the ``<type>val<n>d`` functions for more detail 

500 """ 

501 args = [np.asanyarray(a) for a in args] 

502 shape0 = args[0].shape 

503 if not all((a.shape == shape0 for a in args[1:])): 

504 if len(args) == 3: 

505 raise ValueError('x, y, z are incompatible') 

506 elif len(args) == 2: 

507 raise ValueError('x, y are incompatible') 

508 else: 

509 raise ValueError('ordinates are incompatible') 

510 it = iter(args) 

511 x0 = next(it) 

512 

513 # use tensor on only the first 

514 c = val_f(x0, c) 

515 for xi in it: 

516 c = val_f(xi, c, tensor=False) 

517 return c 

518 

519 

520def _gridnd(val_f, c, *args): 

521 """ 

522 Helper function used to implement the ``<type>grid<n>d`` functions. 

523 

524 Parameters 

525 ---------- 

526 val_f : function(array_like, array_like, tensor: bool) -> array_like 

527 The ``<type>val`` function, such as ``polyval`` 

528 c, args 

529 See the ``<type>grid<n>d`` functions for more detail 

530 """ 

531 for xi in args: 

532 c = val_f(xi, c) 

533 return c 

534 

535 

536def _div(mul_f, c1, c2): 

537 """ 

538 Helper function used to implement the ``<type>div`` functions. 

539 

540 Implementation uses repeated subtraction of c2 multiplied by the nth basis. 

541 For some polynomial types, a more efficient approach may be possible. 

542 

543 Parameters 

544 ---------- 

545 mul_f : function(array_like, array_like) -> array_like 

546 The ``<type>mul`` function, such as ``polymul`` 

547 c1, c2 

548 See the ``<type>div`` functions for more detail 

549 """ 

550 # c1, c2 are trimmed copies 

551 [c1, c2] = as_series([c1, c2]) 

552 if c2[-1] == 0: 

553 raise ZeroDivisionError() 

554 

555 lc1 = len(c1) 

556 lc2 = len(c2) 

557 if lc1 < lc2: 

558 return c1[:1]*0, c1 

559 elif lc2 == 1: 

560 return c1/c2[-1], c1[:1]*0 

561 else: 

562 quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) 

563 rem = c1 

564 for i in range(lc1 - lc2, - 1, -1): 

565 p = mul_f([0]*i + [1], c2) 

566 q = rem[-1]/p[-1] 

567 rem = rem[:-1] - q*p[:-1] 

568 quo[i] = q 

569 return quo, trimseq(rem) 

570 

571 

572def _add(c1, c2): 

573 """ Helper function used to implement the ``<type>add`` functions. """ 

574 # c1, c2 are trimmed copies 

575 [c1, c2] = as_series([c1, c2]) 

576 if len(c1) > len(c2): 

577 c1[:c2.size] += c2 

578 ret = c1 

579 else: 

580 c2[:c1.size] += c1 

581 ret = c2 

582 return trimseq(ret) 

583 

584 

585def _sub(c1, c2): 

586 """ Helper function used to implement the ``<type>sub`` functions. """ 

587 # c1, c2 are trimmed copies 

588 [c1, c2] = as_series([c1, c2]) 

589 if len(c1) > len(c2): 

590 c1[:c2.size] -= c2 

591 ret = c1 

592 else: 

593 c2 = -c2 

594 c2[:c1.size] += c1 

595 ret = c2 

596 return trimseq(ret) 

597 

598 

599def _fit(vander_f, x, y, deg, rcond=None, full=False, w=None): 

600 """ 

601 Helper function used to implement the ``<type>fit`` functions. 

602 

603 Parameters 

604 ---------- 

605 vander_f : function(array_like, int) -> ndarray 

606 The 1d vander function, such as ``polyvander`` 

607 c1, c2 

608 See the ``<type>fit`` functions for more detail 

609 """ 

610 x = np.asarray(x) + 0.0 

611 y = np.asarray(y) + 0.0 

612 deg = np.asarray(deg) 

613 

614 # check arguments. 

615 if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: 

616 raise TypeError("deg must be an int or non-empty 1-D array of int") 

617 if deg.min() < 0: 

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

619 if x.ndim != 1: 

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

621 if x.size == 0: 

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

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

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

625 if len(x) != len(y): 

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

627 

628 if deg.ndim == 0: 

629 lmax = deg 

630 order = lmax + 1 

631 van = vander_f(x, lmax) 

632 else: 

633 deg = np.sort(deg) 

634 lmax = deg[-1] 

635 order = len(deg) 

636 van = vander_f(x, lmax)[:, deg] 

637 

638 # set up the least squares matrices in transposed form 

639 lhs = van.T 

640 rhs = y.T 

641 if w is not None: 

642 w = np.asarray(w) + 0.0 

643 if w.ndim != 1: 

644 raise TypeError("expected 1D vector for w") 

645 if len(x) != len(w): 

646 raise TypeError("expected x and w to have same length") 

647 # apply weights. Don't use inplace operations as they 

648 # can cause problems with NA. 

649 lhs = lhs * w 

650 rhs = rhs * w 

651 

652 # set rcond 

653 if rcond is None: 

654 rcond = len(x)*np.finfo(x.dtype).eps 

655 

656 # Determine the norms of the design matrix columns. 

657 if issubclass(lhs.dtype.type, np.complexfloating): 

658 scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) 

659 else: 

660 scl = np.sqrt(np.square(lhs).sum(1)) 

661 scl[scl == 0] = 1 

662 

663 # Solve the least squares problem. 

664 c, resids, rank, s = np.linalg.lstsq(lhs.T/scl, rhs.T, rcond) 

665 c = (c.T/scl).T 

666 

667 # Expand c to include non-fitted coefficients which are set to zero 

668 if deg.ndim > 0: 

669 if c.ndim == 2: 

670 cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) 

671 else: 

672 cc = np.zeros(lmax+1, dtype=c.dtype) 

673 cc[deg] = c 

674 c = cc 

675 

676 # warn on rank reduction 

677 if rank != order and not full: 

678 msg = "The fit may be poorly conditioned" 

679 warnings.warn(msg, RankWarning, stacklevel=2) 

680 

681 if full: 

682 return c, [resids, rank, s, rcond] 

683 else: 

684 return c 

685 

686 

687def _pow(mul_f, c, pow, maxpower): 

688 """ 

689 Helper function used to implement the ``<type>pow`` functions. 

690 

691 Parameters 

692 ---------- 

693 mul_f : function(array_like, array_like) -> ndarray 

694 The ``<type>mul`` function, such as ``polymul`` 

695 c : array_like 

696 1-D array of array of series coefficients 

697 pow, maxpower 

698 See the ``<type>pow`` functions for more detail 

699 """ 

700 # c is a trimmed copy 

701 [c] = as_series([c]) 

702 power = int(pow) 

703 if power != pow or power < 0: 

704 raise ValueError("Power must be a non-negative integer.") 

705 elif maxpower is not None and power > maxpower: 

706 raise ValueError("Power is too large") 

707 elif power == 0: 

708 return np.array([1], dtype=c.dtype) 

709 elif power == 1: 

710 return c 

711 else: 

712 # This can be made more efficient by using powers of two 

713 # in the usual way. 

714 prd = c 

715 for i in range(2, power + 1): 

716 prd = mul_f(prd, c) 

717 return prd 

718 

719 

720def _deprecate_as_int(x, desc): 

721 """ 

722 Like `operator.index`, but emits a deprecation warning when passed a float 

723 

724 Parameters 

725 ---------- 

726 x : int-like, or float with integral value 

727 Value to interpret as an integer 

728 desc : str 

729 description to include in any error message 

730 

731 Raises 

732 ------ 

733 TypeError : if x is a non-integral float or non-numeric 

734 DeprecationWarning : if x is an integral float 

735 """ 

736 try: 

737 return operator.index(x) 

738 except TypeError as e: 

739 # Numpy 1.17.0, 2019-03-11 

740 try: 

741 ix = int(x) 

742 except TypeError: 

743 pass 

744 else: 

745 if ix == x: 

746 warnings.warn( 

747 f"In future, this will raise TypeError, as {desc} will " 

748 "need to be an integer not just an integral float.", 

749 DeprecationWarning, 

750 stacklevel=3 

751 ) 

752 return ix 

753 

754 raise TypeError(f"{desc} must be an integer") from e 

755 

756 

757def format_float(x, parens=False): 

758 if not np.issubdtype(type(x), np.floating): 

759 return str(x) 

760 

761 opts = np.get_printoptions() 

762 

763 if np.isnan(x): 

764 return opts['nanstr'] 

765 elif np.isinf(x): 

766 return opts['infstr'] 

767 

768 exp_format = False 

769 if x != 0: 

770 a = absolute(x) 

771 if a >= 1.e8 or a < 10**min(0, -(opts['precision']-1)//2): 

772 exp_format = True 

773 

774 trim, unique = '0', True 

775 if opts['floatmode'] == 'fixed': 

776 trim, unique = 'k', False 

777 

778 if exp_format: 

779 s = dragon4_scientific(x, precision=opts['precision'], 

780 unique=unique, trim=trim, 

781 sign=opts['sign'] == '+') 

782 if parens: 

783 s = '(' + s + ')' 

784 else: 

785 s = dragon4_positional(x, precision=opts['precision'], 

786 fractional=True, 

787 unique=unique, trim=trim, 

788 sign=opts['sign'] == '+') 

789 return s