diff options
Diffstat (limited to 'numpy/polynomial')
| -rw-r--r-- | numpy/polynomial/_polybase.py | 77 | ||||
| -rw-r--r-- | numpy/polynomial/chebyshev.py | 217 | ||||
| -rw-r--r-- | numpy/polynomial/hermite.py | 235 | ||||
| -rw-r--r-- | numpy/polynomial/hermite_e.py | 235 | ||||
| -rw-r--r-- | numpy/polynomial/laguerre.py | 234 | ||||
| -rw-r--r-- | numpy/polynomial/legendre.py | 235 | ||||
| -rw-r--r-- | numpy/polynomial/polynomial.py | 229 | ||||
| -rw-r--r-- | numpy/polynomial/polyutils.py | 349 | ||||
| -rw-r--r-- | numpy/polynomial/tests/test_chebyshev.py | 6 | ||||
| -rw-r--r-- | numpy/polynomial/tests/test_hermite.py | 6 | ||||
| -rw-r--r-- | numpy/polynomial/tests/test_hermite_e.py | 6 | ||||
| -rw-r--r-- | numpy/polynomial/tests/test_laguerre.py | 6 | ||||
| -rw-r--r-- | numpy/polynomial/tests/test_legendre.py | 6 | ||||
| -rw-r--r-- | numpy/polynomial/tests/test_polynomial.py | 6 |
14 files changed, 562 insertions, 1285 deletions
diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py index c28e77e69..bfa030714 100644 --- a/numpy/polynomial/_polybase.py +++ b/numpy/polynomial/_polybase.py @@ -8,7 +8,7 @@ abc module from the stdlib, hence it is only available for Python >= 2.6. """ from __future__ import division, absolute_import, print_function -from abc import ABCMeta, abstractmethod, abstractproperty +import abc import numbers import numpy as np @@ -16,7 +16,7 @@ from . import polyutils as pu __all__ = ['ABCPolyBase'] -class ABCPolyBase(object): +class ABCPolyBase(abc.ABC): """An abstract base class for immutable series classes. ABCPolyBase provides the standard Python numerical methods @@ -59,7 +59,6 @@ class ABCPolyBase(object): Default window of the class. """ - __metaclass__ = ABCMeta # Not hashable __hash__ = None @@ -70,68 +69,84 @@ class ABCPolyBase(object): # Limit runaway size. T_n^m has degree n*m maxpower = 100 - @abstractproperty + @property + @abc.abstractmethod def domain(self): pass - @abstractproperty + @property + @abc.abstractmethod def window(self): pass - @abstractproperty + @property + @abc.abstractmethod def nickname(self): pass - @abstractproperty + @property + @abc.abstractmethod def basis_name(self): pass - @abstractmethod - def _add(self): + @staticmethod + @abc.abstractmethod + def _add(c1, c2): pass - @abstractmethod - def _sub(self): + @staticmethod + @abc.abstractmethod + def _sub(c1, c2): pass - @abstractmethod - def _mul(self): + @staticmethod + @abc.abstractmethod + def _mul(c1, c2): pass - @abstractmethod - def _div(self): + @staticmethod + @abc.abstractmethod + def _div(c1, c2): pass - @abstractmethod - def _pow(self): + @staticmethod + @abc.abstractmethod + def _pow(c, pow, maxpower=None): pass - @abstractmethod - def _val(self): + @staticmethod + @abc.abstractmethod + def _val(x, c): pass - @abstractmethod - def _int(self): + @staticmethod + @abc.abstractmethod + def _int(c, m, k, lbnd, scl): pass - @abstractmethod - def _der(self): + @staticmethod + @abc.abstractmethod + def _der(c, m, scl): pass - @abstractmethod - def _fit(self): + @staticmethod + @abc.abstractmethod + def _fit(x, y, deg, rcond, full): pass - @abstractmethod - def _line(self): + @staticmethod + @abc.abstractmethod + def _line(off, scl): pass - @abstractmethod - def _roots(self): + @staticmethod + @abc.abstractmethod + def _roots(c): pass - @abstractmethod - def _fromroots(self): + @staticmethod + @abc.abstractmethod + def _fromroots(r): pass def has_samecoef(self, other): diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index e0734e1b8..c37b2c6d6 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -225,15 +225,15 @@ def _zseries_div(z1, z2): """ z1 = z1.copy() z2 = z2.copy() - len1 = len(z1) - len2 = len(z2) - if len2 == 1: + lc1 = len(z1) + lc2 = len(z2) + if lc2 == 1: z1 /= z2 return z1, z1[:1]*0 - elif len1 < len2: + elif lc1 < lc2: return z1[:1]*0, z1 else: - dlen = len1 - len2 + dlen = lc1 - lc2 scl = z2[0] z2 /= scl quo = np.empty(dlen + 1, dtype=z1.dtype) @@ -244,16 +244,16 @@ def _zseries_div(z1, z2): quo[i] = z1[i] quo[dlen - i] = r tmp = r*z2 - z1[i:i+len2] -= tmp - z1[j:j+len2] -= tmp + z1[i:i+lc2] -= tmp + z1[j:j+lc2] -= tmp i += 1 j -= 1 r = z1[i] quo[i] = r tmp = r*z2 - z1[i:i+len2] -= tmp + z1[i:i+lc2] -= tmp quo /= scl - rem = z1[i+1:i-1+len2].copy() + rem = z1[i+1:i-1+lc2].copy() return quo, rem @@ -541,21 +541,7 @@ def chebfromroots(roots): array([1.5+0.j, 0. +0.j, 0.5+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [chebline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [chebmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = chebmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(chebline, chebmul, roots) def chebadd(c1, c2): @@ -597,15 +583,7 @@ def chebadd(c1, c2): array([4., 4., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def chebsub(c1, c2): @@ -649,16 +627,7 @@ def chebsub(c1, c2): array([ 2., 0., -2.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def chebmulx(c): @@ -807,6 +776,7 @@ def chebdiv(c1, c2): if c2[-1] == 0: raise ZeroDivisionError() + # note: this is more efficient than `pu._div(chebmul, c1, c2)` lc1 = len(c1) lc2 = len(c2) if lc1 < lc2: @@ -856,6 +826,9 @@ def chebpow(c, pow, maxpower=16): array([15.5, 22. , 16. , ..., 12.5, 12. , 8. ]) """ + # note: this is more efficient than `pu._pow(chebmul, c1, c2)`, as it + # avoids converting between z and c series repeatedly + # c is a trimmed copy [c] = pu.as_series([c]) power = int(pow) @@ -940,14 +913,10 @@ def chebder(c, m=1, scl=1, axis=0): c = np.array(c, ndmin=1, copy=1) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -1063,10 +1032,8 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -1075,8 +1042,6 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -1238,14 +1203,7 @@ def chebval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = chebval(x, c) - c = chebval(y, c, tensor=False) - return c + return pu._valnd(chebval, c, x, y) def chebgrid2d(x, y, c): @@ -1298,9 +1256,7 @@ def chebgrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = chebval(x, c) - c = chebval(y, c) - return c + return pu._gridnd(chebval, c, x, y) def chebval3d(x, y, z, c): @@ -1351,15 +1307,7 @@ def chebval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = chebval(x, c) - c = chebval(y, c, tensor=False) - c = chebval(z, c, tensor=False) - return c + return pu._valnd(chebval, c, x, y, z) def chebgrid3d(x, y, z, c): @@ -1415,10 +1363,7 @@ def chebgrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = chebval(x, c) - c = chebval(y, c) - c = chebval(z, c) - return c + return pu._gridnd(chebval, c, x, y, z) def chebvander(x, deg): @@ -1456,9 +1401,7 @@ def chebvander(x, deg): the converted `x`. """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") @@ -1526,17 +1469,7 @@ def chebvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = chebvander(x, degx) - vy = chebvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander2d(chebvander, x, y, deg) def chebvander3d(x, y, z, deg): @@ -1590,18 +1523,7 @@ def chebvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = chebvander(x, degx) - vy = chebvander(y, degy) - vz = chebvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander3d(chebvander, x, y, z, deg) def chebfit(x, y, deg, rcond=None, full=False, w=None): @@ -1723,81 +1645,7 @@ def chebfit(x, y, deg, rcond=None, full=False, w=None): -------- """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = chebvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = chebvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax + 1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax + 1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(chebvander, x, y, deg, rcond, full, w) def chebcompanion(c): @@ -1895,7 +1743,8 @@ def chebroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = chebcompanion(c) + # rotated companion matrix reduces error + m = chebcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -2003,9 +1852,9 @@ def chebgauss(deg): .. math:: w_i = \\pi / n """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") x = np.cos(np.pi * np.arange(1, 2*ideg, 2) / (2.0*ideg)) w = np.ones(ideg)*(np.pi/ideg) diff --git a/numpy/polynomial/hermite.py b/numpy/polynomial/hermite.py index 93c9fc564..3870d1054 100644 --- a/numpy/polynomial/hermite.py +++ b/numpy/polynomial/hermite.py @@ -286,21 +286,7 @@ def hermfromroots(roots): array([0.+0.j, 0.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [hermline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [hermmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = hermmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(hermline, hermmul, roots) def hermadd(c1, c2): @@ -340,15 +326,7 @@ def hermadd(c1, c2): array([2., 4., 6., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def hermsub(c1, c2): @@ -388,16 +366,7 @@ def hermsub(c1, c2): array([0., 0., 0., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def hermmulx(c): @@ -564,26 +533,7 @@ def hermdiv(c1, c2): (array([1., 2., 3.]), array([1., 1.])) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = hermmul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(hermmul, c1, c2) def hermpow(c, pow, maxpower=16): @@ -620,24 +570,7 @@ def hermpow(c, pow, maxpower=16): array([81., 52., 82., 12., 9.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = hermmul(prd, c) - return prd + return pu._pow(hermmul, c, pow, maxpower) def hermder(c, m=1, scl=1, axis=0): @@ -698,14 +631,10 @@ def hermder(c, m=1, scl=1, axis=0): c = np.array(c, ndmin=1, copy=1) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -815,10 +744,8 @@ def hermint(c, m=1, k=[], lbnd=0, scl=1, axis=0): c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -827,8 +754,6 @@ def hermint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -995,14 +920,7 @@ def hermval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = hermval(x, c) - c = hermval(y, c, tensor=False) - return c + return pu._valnd(hermval, c, x, y) def hermgrid2d(x, y, c): @@ -1055,9 +973,7 @@ def hermgrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = hermval(x, c) - c = hermval(y, c) - return c + return pu._gridnd(hermval, c, x, y) def hermval3d(x, y, z, c): @@ -1108,15 +1024,7 @@ def hermval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = hermval(x, c) - c = hermval(y, c, tensor=False) - c = hermval(z, c, tensor=False) - return c + return pu._valnd(hermval, c, x, y, z) def hermgrid3d(x, y, z, c): @@ -1172,10 +1080,7 @@ def hermgrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = hermval(x, c) - c = hermval(y, c) - c = hermval(z, c) - return c + return pu._gridnd(hermval, c, x, y, z) def hermvander(x, deg): @@ -1222,9 +1127,7 @@ def hermvander(x, deg): [ 1., 2., 2., -4.]]) """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") @@ -1291,17 +1194,7 @@ def hermvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = hermvander(x, degx) - vy = hermvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander2d(hermvander, x, y, deg) def hermvander3d(x, y, z, deg): @@ -1355,18 +1248,7 @@ def hermvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = hermvander(x, degx) - vy = hermvander(y, degy) - vz = hermvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander3d(hermvander, x, y, z, deg) def hermfit(x, y, deg, rcond=None, full=False, w=None): @@ -1493,81 +1375,7 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): array([1.0218, 1.9986, 2.9999]) # may vary """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = hermvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = hermvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(hermvander, x, y, deg, rcond, full, w) def hermcompanion(c): @@ -1668,7 +1476,8 @@ def hermroots(c): if len(c) == 2: return np.array([-.5*c[0]/c[1]]) - m = hermcompanion(c) + # rotated companion matrix reduces error + m = hermcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1753,9 +1562,9 @@ def hermgauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. diff --git a/numpy/polynomial/hermite_e.py b/numpy/polynomial/hermite_e.py index bafb4b997..b12c0d792 100644 --- a/numpy/polynomial/hermite_e.py +++ b/numpy/polynomial/hermite_e.py @@ -287,21 +287,7 @@ def hermefromroots(roots): array([0.+0.j, 0.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [hermeline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [hermemul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = hermemul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(hermeline, hermemul, roots) def hermeadd(c1, c2): @@ -341,15 +327,7 @@ def hermeadd(c1, c2): array([2., 4., 6., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def hermesub(c1, c2): @@ -389,16 +367,7 @@ def hermesub(c1, c2): array([0., 0., 0., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def hermemulx(c): @@ -559,26 +528,7 @@ def hermediv(c1, c2): (array([1., 2., 3.]), array([1., 2.])) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = hermemul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(hermemul, c1, c2) def hermepow(c, pow, maxpower=16): @@ -615,24 +565,7 @@ def hermepow(c, pow, maxpower=16): array([23., 28., 46., 12., 9.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = hermemul(prd, c) - return prd + return pu._pow(hermemul, c, pow, maxpower) def hermeder(c, m=1, scl=1, axis=0): @@ -693,14 +626,10 @@ def hermeder(c, m=1, scl=1, axis=0): c = np.array(c, ndmin=1, copy=1) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -810,10 +739,8 @@ def hermeint(c, m=1, k=[], lbnd=0, scl=1, axis=0): c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -822,8 +749,6 @@ def hermeint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -989,14 +914,7 @@ def hermeval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = hermeval(x, c) - c = hermeval(y, c, tensor=False) - return c + return pu._valnd(hermeval, c, x, y) def hermegrid2d(x, y, c): @@ -1049,9 +967,7 @@ def hermegrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = hermeval(x, c) - c = hermeval(y, c) - return c + return pu._gridnd(hermeval, c, x, y) def hermeval3d(x, y, z, c): @@ -1102,15 +1018,7 @@ def hermeval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = hermeval(x, c) - c = hermeval(y, c, tensor=False) - c = hermeval(z, c, tensor=False) - return c + return pu._valnd(hermeval, c, x, y, z) def hermegrid3d(x, y, z, c): @@ -1166,10 +1074,7 @@ def hermegrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = hermeval(x, c) - c = hermeval(y, c) - c = hermeval(z, c) - return c + return pu._gridnd(hermeval, c, x, y, z) def hermevander(x, deg): @@ -1216,9 +1121,7 @@ def hermevander(x, deg): [ 1., 1., 0., -2.]]) """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") @@ -1284,17 +1187,7 @@ def hermevander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = hermevander(x, degx) - vy = hermevander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander2d(hermevander, x, y, deg) def hermevander3d(x, y, z, deg): @@ -1348,18 +1241,7 @@ def hermevander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = hermevander(x, degx) - vy = hermevander(y, degy) - vz = hermevander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander3d(hermevander, x, y, z, deg) def hermefit(x, y, deg, rcond=None, full=False, w=None): @@ -1487,81 +1369,7 @@ def hermefit(x, y, deg, rcond=None, full=False, w=None): array([ 1.01690445, 1.99951418, 2.99948696]) # may vary """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = hermevander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = hermevander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(hermevander, x, y, deg, rcond, full, w) def hermecompanion(c): @@ -1663,7 +1471,8 @@ def hermeroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = hermecompanion(c) + # rotated companion matrix reduces error + m = hermecompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1748,9 +1557,9 @@ def hermegauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. diff --git a/numpy/polynomial/laguerre.py b/numpy/polynomial/laguerre.py index 9207c9afe..e103dde92 100644 --- a/numpy/polynomial/laguerre.py +++ b/numpy/polynomial/laguerre.py @@ -283,21 +283,7 @@ def lagfromroots(roots): array([0.+0.j, 0.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [lagline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [lagmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = lagmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(lagline, lagmul, roots) def lagadd(c1, c2): @@ -338,15 +324,7 @@ def lagadd(c1, c2): """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def lagsub(c1, c2): @@ -386,16 +364,7 @@ def lagsub(c1, c2): array([0., 0., 0., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def lagmulx(c): @@ -561,26 +530,7 @@ def lagdiv(c1, c2): (array([1., 2., 3.]), array([1., 1.])) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = lagmul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(lagmul, c1, c2) def lagpow(c, pow, maxpower=16): @@ -617,24 +567,7 @@ def lagpow(c, pow, maxpower=16): array([ 14., -16., 56., -72., 54.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = lagmul(prd, c) - return prd + return pu._pow(lagmul, c, pow, maxpower) def lagder(c, m=1, scl=1, axis=0): @@ -695,14 +628,11 @@ def lagder(c, m=1, scl=1, axis=0): c = np.array(c, ndmin=1, copy=1) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -815,10 +745,8 @@ def lagint(c, m=1, k=[], lbnd=0, scl=1, axis=0): c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -827,8 +755,6 @@ def lagint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -995,14 +921,7 @@ def lagval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = lagval(x, c) - c = lagval(y, c, tensor=False) - return c + return pu._valnd(lagval, c, x, y) def laggrid2d(x, y, c): @@ -1055,9 +974,7 @@ def laggrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = lagval(x, c) - c = lagval(y, c) - return c + return pu._gridnd(lagval, c, x, y) def lagval3d(x, y, z, c): @@ -1108,15 +1025,7 @@ def lagval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = lagval(x, c) - c = lagval(y, c, tensor=False) - c = lagval(z, c, tensor=False) - return c + return pu._valnd(lagval, c, x, y, z) def laggrid3d(x, y, z, c): @@ -1172,10 +1081,7 @@ def laggrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = lagval(x, c) - c = lagval(y, c) - c = lagval(z, c) - return c + return pu._gridnd(lagval, c, x, y, z) def lagvander(x, deg): @@ -1222,9 +1128,7 @@ def lagvander(x, deg): [ 1. , -1. , -1. , -0.33333333]]) """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") @@ -1290,17 +1194,7 @@ def lagvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = lagvander(x, degx) - vy = lagvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander2d(lagvander, x, y, deg) def lagvander3d(x, y, z, deg): @@ -1354,18 +1248,7 @@ def lagvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = lagvander(x, degx) - vy = lagvander(y, degy) - vz = lagvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander3d(lagvander, x, y, z, deg) def lagfit(x, y, deg, rcond=None, full=False, w=None): @@ -1492,81 +1375,7 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): array([ 0.96971004, 2.00193749, 3.00288744]) # may vary """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = lagvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = lagvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(lagvander, x, y, deg, rcond, full, w) def lagcompanion(c): @@ -1666,7 +1475,8 @@ def lagroots(c): if len(c) == 2: return np.array([1 + c[0]/c[1]]) - m = lagcompanion(c) + # rotated companion matrix reduces error + m = lagcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1708,9 +1518,9 @@ def laggauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. diff --git a/numpy/polynomial/legendre.py b/numpy/polynomial/legendre.py index f81bc002c..9eec9740d 100644 --- a/numpy/polynomial/legendre.py +++ b/numpy/polynomial/legendre.py @@ -314,21 +314,7 @@ def legfromroots(roots): array([ 1.33333333+0.j, 0.00000000+0.j, 0.66666667+0.j]) # may vary """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [legline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [legmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = legmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(legline, legmul, roots) def legadd(c1, c2): @@ -370,15 +356,7 @@ def legadd(c1, c2): array([4., 4., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def legsub(c1, c2): @@ -422,16 +400,7 @@ def legsub(c1, c2): array([ 2., 0., -2.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def legmulx(c): @@ -604,26 +573,7 @@ def legdiv(c1, c2): (array([-0.07407407, 1.66666667]), array([-1.03703704, -2.51851852])) # may vary """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = legmul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(legmul, c1, c2) def legpow(c, pow, maxpower=16): @@ -657,24 +607,7 @@ def legpow(c, pow, maxpower=16): -------- """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = legmul(prd, c) - return prd + return pu._pow(legmul, c, pow, maxpower) def legder(c, m=1, scl=1, axis=0): @@ -740,14 +673,10 @@ def legder(c, m=1, scl=1, axis=0): c = np.array(c, ndmin=1, copy=1) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -863,10 +792,8 @@ def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0): c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -875,8 +802,6 @@ def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -1039,14 +964,7 @@ def legval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = legval(x, c) - c = legval(y, c, tensor=False) - return c + return pu._valnd(legval, c, x, y) def leggrid2d(x, y, c): @@ -1099,9 +1017,7 @@ def leggrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = legval(x, c) - c = legval(y, c) - return c + return pu._gridnd(legval, c, x, y) def legval3d(x, y, z, c): @@ -1152,15 +1068,7 @@ def legval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = legval(x, c) - c = legval(y, c, tensor=False) - c = legval(z, c, tensor=False) - return c + return pu._valnd(legval, c, x, y, z) def leggrid3d(x, y, z, c): @@ -1216,10 +1124,7 @@ def leggrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = legval(x, c) - c = legval(y, c) - c = legval(z, c) - return c + return pu._gridnd(legval, c, x, y, z) def legvander(x, deg): @@ -1257,9 +1162,7 @@ def legvander(x, deg): the converted `x`. """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") @@ -1327,17 +1230,7 @@ def legvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = legvander(x, degx) - vy = legvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander2d(legvander, x, y, deg) def legvander3d(x, y, z, deg): @@ -1391,18 +1284,7 @@ def legvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = legvander(x, degx) - vy = legvander(y, degy) - vz = legvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander3d(legvander, x, y, z, deg) def legfit(x, y, deg, rcond=None, full=False, w=None): @@ -1526,81 +1408,7 @@ def legfit(x, y, deg, rcond=None, full=False, w=None): -------- """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = legvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = legvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(legvander, x, y, deg, rcond, full, w) def legcompanion(c): @@ -1697,7 +1505,8 @@ def legroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = legcompanion(c) + # rotated companion matrix reduces error + m = legcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1739,9 +1548,9 @@ def leggauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index 69599e3fd..afa330502 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -188,21 +188,7 @@ def polyfromroots(roots): array([1.+0.j, 0.+0.j, 1.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [polyline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [polymul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = polymul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(polyline, polymul, roots) def polyadd(c1, c2): @@ -238,15 +224,7 @@ def polyadd(c1, c2): 28.0 """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def polysub(c1, c2): @@ -283,16 +261,7 @@ def polysub(c1, c2): array([ 2., 0., -2.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def polymulx(c): @@ -411,18 +380,19 @@ def polydiv(c1, c2): if c2[-1] == 0: raise ZeroDivisionError() - len1 = len(c1) - len2 = len(c2) - if len2 == 1: - return c1/c2[-1], c1[:1]*0 - elif len1 < len2: + # note: this is more efficient than `pu._div(polymul, c1, c2)` + lc1 = len(c1) + lc2 = len(c2) + if lc1 < lc2: return c1[:1]*0, c1 + elif lc2 == 1: + return c1/c2[-1], c1[:1]*0 else: - dlen = len1 - len2 + dlen = lc1 - lc2 scl = c2[-1] c2 = c2[:-1]/scl i = dlen - j = len1 - 1 + j = lc1 - 1 while i >= 0: c1[i:j] -= c2*c1[j] i -= 1 @@ -464,24 +434,9 @@ def polypow(c, pow, maxpower=None): array([ 1., 4., 10., 12., 9.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = np.convolve(prd, c) - return prd + # note: this is more efficient than `pu._pow(polymul, c1, c2)`, as it + # avoids calling `as_series` repeatedly + return pu._pow(np.convolve, c, pow, maxpower) def polyder(c, m=1, scl=1, axis=0): @@ -541,14 +496,10 @@ def polyder(c, m=1, scl=1, axis=0): # astype fails with NA c = c + 0.0 cdt = c.dtype - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -655,10 +606,8 @@ def polyint(c, m=1, k=[], lbnd=0, scl=1, axis=0): cdt = c.dtype if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -667,8 +616,6 @@ def polyint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -924,14 +871,7 @@ def polyval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = polyval(x, c) - c = polyval(y, c, tensor=False) - return c + return pu._valnd(polyval, c, x, y) def polygrid2d(x, y, c): @@ -984,9 +924,7 @@ def polygrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = polyval(x, c) - c = polyval(y, c) - return c + return pu._gridnd(polyval, c, x, y) def polyval3d(x, y, z, c): @@ -1037,15 +975,7 @@ def polyval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = polyval(x, c) - c = polyval(y, c, tensor=False) - c = polyval(z, c, tensor=False) - return c + return pu._valnd(polyval, c, x, y, z) def polygrid3d(x, y, z, c): @@ -1101,10 +1031,7 @@ def polygrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = polyval(x, c) - c = polyval(y, c) - c = polyval(z, c) - return c + return pu._gridnd(polyval, c, x, y, z) def polyvander(x, deg): @@ -1145,9 +1072,7 @@ def polyvander(x, deg): polyvander2d, polyvander3d """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") @@ -1208,19 +1133,7 @@ def polyvander2d(x, y, deg): polyvander, polyvander3d. polyval2d, polyval3d """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = polyvander(x, degx) - vy = polyvander(y, degy) - v = vx[..., None]*vy[..., None,:] - # einsum bug - #v = np.einsum("...i,...j->...ij", vx, vy) - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander2d(polyvander, x, y, deg) def polyvander3d(x, y, z, deg): @@ -1274,20 +1187,7 @@ def polyvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = polyvander(x, degx) - vy = polyvander(y, degy) - vz = polyvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - # einsum bug - #v = np.einsum("...i, ...j, ...k->...ijk", vx, vy, vz) - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander3d(polyvander, x, y, z, deg) def polyfit(x, y, deg, rcond=None, full=False, w=None): @@ -1433,81 +1333,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): 0.50443316, 0.28853036]), 1.1324274851176597e-014] """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = polyvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = polyvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim == 1: - if c.ndim == 2: - cc = np.zeros((lmax + 1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax + 1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(polyvander, x, y, deg, rcond, full, w) def polycompanion(c): @@ -1602,7 +1428,8 @@ def polyroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = polycompanion(c) + # rotated companion matrix reduces error + m = polycompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r diff --git a/numpy/polynomial/polyutils.py b/numpy/polynomial/polyutils.py index eff4a8ee1..5128e35e0 100644 --- a/numpy/polynomial/polyutils.py +++ b/numpy/polynomial/polyutils.py @@ -45,6 +45,8 @@ Functions """ from __future__ import division, absolute_import, print_function +import operator + import numpy as np __all__ = [ @@ -410,3 +412,350 @@ def mapdomain(x, old, new): x = np.asanyarray(x) off, scl = mapparms(old, new) return off + scl*x + + +def _vander2d(vander_f, x, y, deg): + """ + Helper function used to implement the ``<type>vander2d`` functions. + + Parameters + ---------- + vander_f : function(array_like, int) -> ndarray + The 1d vander function, such as ``polyvander`` + x, y, deg : + See the ``<type>vander2d`` functions for more detail + """ + degx, degy = [ + _deprecate_as_int(d, "degrees") + for d in deg + ] + x, y = np.array((x, y), copy=0) + 0.0 + + vx = vander_f(x, degx) + vy = vander_f(y, degy) + v = vx[..., None]*vy[..., None,:] + return v.reshape(v.shape[:-2] + (-1,)) + + +def _vander3d(vander_f, x, y, z, deg): + """ + Helper function used to implement the ``<type>vander3d`` functions. + + Parameters + ---------- + vander_f : function(array_like, int) -> ndarray + The 1d vander function, such as ``polyvander`` + x, y, z, deg : + See the ``<type>vander3d`` functions for more detail + """ + degx, degy, degz = [ + _deprecate_as_int(d, "degrees") + for d in deg + ] + x, y, z = np.array((x, y, z), copy=0) + 0.0 + + vx = vander_f(x, degx) + vy = vander_f(y, degy) + vz = vander_f(z, degz) + v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] + return v.reshape(v.shape[:-3] + (-1,)) + + +def _fromroots(line_f, mul_f, roots): + """ + Helper function used to implement the ``<type>fromroots`` functions. + + Parameters + ---------- + line_f : function(float, float) -> ndarray + The ``<type>line`` function, such as ``polyline`` + mul_f : function(array_like, array_like) -> ndarray + The ``<type>mul`` function, such as ``polymul`` + roots : + See the ``<type>fromroots`` functions for more detail + """ + if len(roots) == 0: + return np.ones(1) + else: + [roots] = as_series([roots], trim=False) + roots.sort() + p = [line_f(-r, 1) for r in roots] + n = len(p) + while n > 1: + m, r = divmod(n, 2) + tmp = [mul_f(p[i], p[i+m]) for i in range(m)] + if r: + tmp[0] = mul_f(tmp[0], p[-1]) + p = tmp + n = m + return p[0] + + +def _valnd(val_f, c, *args): + """ + Helper function used to implement the ``<type>val<n>d`` functions. + + Parameters + ---------- + val_f : function(array_like, array_like, tensor: bool) -> array_like + The ``<type>val`` function, such as ``polyval`` + c, args : + See the ``<type>val<n>d`` functions for more detail + """ + try: + args = tuple(np.array(args, copy=False)) + except Exception: + # preserve the old error message + if len(args) == 2: + raise ValueError('x, y, z are incompatible') + elif len(args) == 3: + raise ValueError('x, y are incompatible') + else: + raise ValueError('ordinates are incompatible') + + it = iter(args) + x0 = next(it) + + # use tensor on only the first + c = val_f(x0, c) + for xi in it: + c = val_f(xi, c, tensor=False) + return c + + +def _gridnd(val_f, c, *args): + """ + Helper function used to implement the ``<type>grid<n>d`` functions. + + Parameters + ---------- + val_f : function(array_like, array_like, tensor: bool) -> array_like + The ``<type>val`` function, such as ``polyval`` + c, args : + See the ``<type>grid<n>d`` functions for more detail + """ + for xi in args: + c = val_f(xi, c) + return c + + +def _div(mul_f, c1, c2): + """ + Helper function used to implement the ``<type>div`` functions. + + Implementation uses repeated subtraction of c2 multiplied by the nth basis. + For some polynomial types, a more efficient approach may be possible. + + Parameters + ---------- + mul_f : function(array_like, array_like) -> array_like + The ``<type>mul`` function, such as ``polymul`` + c1, c2 : + See the ``<type>div`` functions for more detail + """ + # c1, c2 are trimmed copies + [c1, c2] = as_series([c1, c2]) + if c2[-1] == 0: + raise ZeroDivisionError() + + lc1 = len(c1) + lc2 = len(c2) + if lc1 < lc2: + return c1[:1]*0, c1 + elif lc2 == 1: + return c1/c2[-1], c1[:1]*0 + else: + quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) + rem = c1 + for i in range(lc1 - lc2, - 1, -1): + p = mul_f([0]*i + [1], c2) + q = rem[-1]/p[-1] + rem = rem[:-1] - q*p[:-1] + quo[i] = q + return quo, trimseq(rem) + + +def _add(c1, c2): + """ Helper function used to implement the ``<type>add`` functions. """ + # c1, c2 are trimmed copies + [c1, c2] = as_series([c1, c2]) + if len(c1) > len(c2): + c1[:c2.size] += c2 + ret = c1 + else: + c2[:c1.size] += c1 + ret = c2 + return trimseq(ret) + + +def _sub(c1, c2): + """ Helper function used to implement the ``<type>sub`` functions. """ + # c1, c2 are trimmed copies + [c1, c2] = as_series([c1, c2]) + if len(c1) > len(c2): + c1[:c2.size] -= c2 + ret = c1 + else: + c2 = -c2 + c2[:c1.size] += c1 + ret = c2 + return trimseq(ret) + + +def _fit(vander_f, x, y, deg, rcond=None, full=False, w=None): + """ + Helper function used to implement the ``<type>fit`` functions. + + Parameters + ---------- + vander_f : function(array_like, int) -> ndarray + The 1d vander function, such as ``polyvander`` + c1, c2 : + See the ``<type>fit`` functions for more detail + """ + x = np.asarray(x) + 0.0 + y = np.asarray(y) + 0.0 + deg = np.asarray(deg) + + # check arguments. + if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: + raise TypeError("deg must be an int or non-empty 1-D array of int") + if deg.min() < 0: + raise ValueError("expected deg >= 0") + if x.ndim != 1: + raise TypeError("expected 1D vector for x") + if x.size == 0: + raise TypeError("expected non-empty vector for x") + if y.ndim < 1 or y.ndim > 2: + raise TypeError("expected 1D or 2D array for y") + if len(x) != len(y): + raise TypeError("expected x and y to have same length") + + if deg.ndim == 0: + lmax = deg + order = lmax + 1 + van = vander_f(x, lmax) + else: + deg = np.sort(deg) + lmax = deg[-1] + order = len(deg) + van = vander_f(x, lmax)[:, deg] + + # set up the least squares matrices in transposed form + lhs = van.T + rhs = y.T + if w is not None: + w = np.asarray(w) + 0.0 + if w.ndim != 1: + raise TypeError("expected 1D vector for w") + if len(x) != len(w): + raise TypeError("expected x and w to have same length") + # apply weights. Don't use inplace operations as they + # can cause problems with NA. + lhs = lhs * w + rhs = rhs * w + + # set rcond + if rcond is None: + rcond = len(x)*np.finfo(x.dtype).eps + + # Determine the norms of the design matrix columns. + if issubclass(lhs.dtype.type, np.complexfloating): + scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) + else: + scl = np.sqrt(np.square(lhs).sum(1)) + scl[scl == 0] = 1 + + # Solve the least squares problem. + c, resids, rank, s = np.linalg.lstsq(lhs.T/scl, rhs.T, rcond) + c = (c.T/scl).T + + # Expand c to include non-fitted coefficients which are set to zero + if deg.ndim > 0: + if c.ndim == 2: + cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) + else: + cc = np.zeros(lmax+1, dtype=c.dtype) + cc[deg] = c + c = cc + + # warn on rank reduction + if rank != order and not full: + msg = "The fit may be poorly conditioned" + warnings.warn(msg, RankWarning, stacklevel=2) + + if full: + return c, [resids, rank, s, rcond] + else: + return c + + +def _pow(mul_f, c, pow, maxpower): + """ + Helper function used to implement the ``<type>pow`` functions. + + Parameters + ---------- + vander_f : function(array_like, int) -> ndarray + The 1d vander function, such as ``polyvander`` + pow, maxpower : + See the ``<type>pow`` functions for more detail + mul_f : function(array_like, array_like) -> ndarray + The ``<type>mul`` function, such as ``polymul`` + """ + # c is a trimmed copy + [c] = as_series([c]) + power = int(pow) + if power != pow or power < 0: + raise ValueError("Power must be a non-negative integer.") + elif maxpower is not None and power > maxpower: + raise ValueError("Power is too large") + elif power == 0: + return np.array([1], dtype=c.dtype) + elif power == 1: + return c + else: + # This can be made more efficient by using powers of two + # in the usual way. + prd = c + for i in range(2, power + 1): + prd = mul_f(prd, c) + return prd + + +def _deprecate_as_int(x, desc): + """ + Like `operator.index`, but emits a deprecation warning when passed a float + + Parameters + ---------- + x : int-like, or float with integral value + Value to interpret as an integer + desc : str + description to include in any error message + + Raises + ------ + TypeError : if x is a non-integral float or non-numeric + DeprecationWarning : if x is an integral float + """ + try: + return operator.index(x) + except TypeError: + # Numpy 1.17.0, 2019-03-11 + try: + ix = int(x) + except TypeError: + pass + else: + if ix == x: + warnings.warn( + "In future, this will raise TypeError, as {} will need to " + "be an integer not just an integral float." + .format(desc), + DeprecationWarning, + stacklevel=3 + ) + return ix + + raise TypeError("{} must be an integer".format(desc)) diff --git a/numpy/polynomial/tests/test_chebyshev.py b/numpy/polynomial/tests/test_chebyshev.py index 7fb7492c6..c8d2d6dba 100644 --- a/numpy/polynomial/tests/test_chebyshev.py +++ b/numpy/polynomial/tests/test_chebyshev.py @@ -221,12 +221,12 @@ class TestIntegral(object): def test_chebint(self): # check exceptions - assert_raises(ValueError, cheb.chebint, [0], .5) + assert_raises(TypeError, cheb.chebint, [0], .5) assert_raises(ValueError, cheb.chebint, [0], -1) assert_raises(ValueError, cheb.chebint, [0], 1, [0, 0]) assert_raises(ValueError, cheb.chebint, [0], lbnd=[0]) assert_raises(ValueError, cheb.chebint, [0], scl=[0]) - assert_raises(ValueError, cheb.chebint, [0], axis=.5) + assert_raises(TypeError, cheb.chebint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -323,7 +323,7 @@ class TestDerivative(object): def test_chebder(self): # check exceptions - assert_raises(ValueError, cheb.chebder, [0], .5) + assert_raises(TypeError, cheb.chebder, [0], .5) assert_raises(ValueError, cheb.chebder, [0], -1) # check that zeroth derivative does nothing diff --git a/numpy/polynomial/tests/test_hermite.py b/numpy/polynomial/tests/test_hermite.py index 1287ef3fe..271c1964b 100644 --- a/numpy/polynomial/tests/test_hermite.py +++ b/numpy/polynomial/tests/test_hermite.py @@ -209,12 +209,12 @@ class TestIntegral(object): def test_hermint(self): # check exceptions - assert_raises(ValueError, herm.hermint, [0], .5) + assert_raises(TypeError, herm.hermint, [0], .5) assert_raises(ValueError, herm.hermint, [0], -1) assert_raises(ValueError, herm.hermint, [0], 1, [0, 0]) assert_raises(ValueError, herm.hermint, [0], lbnd=[0]) assert_raises(ValueError, herm.hermint, [0], scl=[0]) - assert_raises(ValueError, herm.hermint, [0], axis=.5) + assert_raises(TypeError, herm.hermint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -311,7 +311,7 @@ class TestDerivative(object): def test_hermder(self): # check exceptions - assert_raises(ValueError, herm.hermder, [0], .5) + assert_raises(TypeError, herm.hermder, [0], .5) assert_raises(ValueError, herm.hermder, [0], -1) # check that zeroth derivative does nothing diff --git a/numpy/polynomial/tests/test_hermite_e.py b/numpy/polynomial/tests/test_hermite_e.py index ccb44ad73..434b30e7b 100644 --- a/numpy/polynomial/tests/test_hermite_e.py +++ b/numpy/polynomial/tests/test_hermite_e.py @@ -209,12 +209,12 @@ class TestIntegral(object): def test_hermeint(self): # check exceptions - assert_raises(ValueError, herme.hermeint, [0], .5) + assert_raises(TypeError, herme.hermeint, [0], .5) assert_raises(ValueError, herme.hermeint, [0], -1) assert_raises(ValueError, herme.hermeint, [0], 1, [0, 0]) assert_raises(ValueError, herme.hermeint, [0], lbnd=[0]) assert_raises(ValueError, herme.hermeint, [0], scl=[0]) - assert_raises(ValueError, herme.hermeint, [0], axis=.5) + assert_raises(TypeError, herme.hermeint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -311,7 +311,7 @@ class TestDerivative(object): def test_hermeder(self): # check exceptions - assert_raises(ValueError, herme.hermeder, [0], .5) + assert_raises(TypeError, herme.hermeder, [0], .5) assert_raises(ValueError, herme.hermeder, [0], -1) # check that zeroth derivative does nothing diff --git a/numpy/polynomial/tests/test_laguerre.py b/numpy/polynomial/tests/test_laguerre.py index 3ababec5e..4b9b28637 100644 --- a/numpy/polynomial/tests/test_laguerre.py +++ b/numpy/polynomial/tests/test_laguerre.py @@ -206,12 +206,12 @@ class TestIntegral(object): def test_lagint(self): # check exceptions - assert_raises(ValueError, lag.lagint, [0], .5) + assert_raises(TypeError, lag.lagint, [0], .5) assert_raises(ValueError, lag.lagint, [0], -1) assert_raises(ValueError, lag.lagint, [0], 1, [0, 0]) assert_raises(ValueError, lag.lagint, [0], lbnd=[0]) assert_raises(ValueError, lag.lagint, [0], scl=[0]) - assert_raises(ValueError, lag.lagint, [0], axis=.5) + assert_raises(TypeError, lag.lagint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -308,7 +308,7 @@ class TestDerivative(object): def test_lagder(self): # check exceptions - assert_raises(ValueError, lag.lagder, [0], .5) + assert_raises(TypeError, lag.lagder, [0], .5) assert_raises(ValueError, lag.lagder, [0], -1) # check that zeroth derivative does nothing diff --git a/numpy/polynomial/tests/test_legendre.py b/numpy/polynomial/tests/test_legendre.py index a23086d59..917a7e03a 100644 --- a/numpy/polynomial/tests/test_legendre.py +++ b/numpy/polynomial/tests/test_legendre.py @@ -210,12 +210,12 @@ class TestIntegral(object): def test_legint(self): # check exceptions - assert_raises(ValueError, leg.legint, [0], .5) + assert_raises(TypeError, leg.legint, [0], .5) assert_raises(ValueError, leg.legint, [0], -1) assert_raises(ValueError, leg.legint, [0], 1, [0, 0]) assert_raises(ValueError, leg.legint, [0], lbnd=[0]) assert_raises(ValueError, leg.legint, [0], scl=[0]) - assert_raises(ValueError, leg.legint, [0], axis=.5) + assert_raises(TypeError, leg.legint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -312,7 +312,7 @@ class TestDerivative(object): def test_legder(self): # check exceptions - assert_raises(ValueError, leg.legder, [0], .5) + assert_raises(TypeError, leg.legder, [0], .5) assert_raises(ValueError, leg.legder, [0], -1) # check that zeroth derivative does nothing diff --git a/numpy/polynomial/tests/test_polynomial.py b/numpy/polynomial/tests/test_polynomial.py index 562aa904d..08b73da15 100644 --- a/numpy/polynomial/tests/test_polynomial.py +++ b/numpy/polynomial/tests/test_polynomial.py @@ -291,12 +291,12 @@ class TestIntegral(object): def test_polyint(self): # check exceptions - assert_raises(ValueError, poly.polyint, [0], .5) + assert_raises(TypeError, poly.polyint, [0], .5) assert_raises(ValueError, poly.polyint, [0], -1) assert_raises(ValueError, poly.polyint, [0], 1, [0, 0]) assert_raises(ValueError, poly.polyint, [0], lbnd=[0]) assert_raises(ValueError, poly.polyint, [0], scl=[0]) - assert_raises(ValueError, poly.polyint, [0], axis=.5) + assert_raises(TypeError, poly.polyint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -388,7 +388,7 @@ class TestDerivative(object): def test_polyder(self): # check exceptions - assert_raises(ValueError, poly.polyder, [0], .5) + assert_raises(TypeError, poly.polyder, [0], .5) assert_raises(ValueError, poly.polyder, [0], -1) # check that zeroth derivative does nothing |
