Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/numpy/polynomial/polyutils.py
7757 views
"""1Utility classes and functions for the polynomial modules.23This module provides: error and warning objects; a polynomial base class;4and some routines used in both the `polynomial` and `chebyshev` modules.56Warning objects7---------------89.. autosummary::10:toctree: generated/1112RankWarning raised in least-squares fit for rank-deficient matrix.1314Functions15---------1617.. autosummary::18:toctree: generated/1920as_series convert list of array_likes into 1-D arrays of common type.21trimseq remove trailing zeros.22trimcoef remove small trailing coefficients.23getdomain return the domain appropriate for a given set of abscissae.24mapdomain maps points between domains.25mapparms parameters of the linear map between domains.2627"""28import operator29import functools30import warnings3132import numpy as np3334__all__ = [35'RankWarning', 'as_series', 'trimseq',36'trimcoef', 'getdomain', 'mapdomain', 'mapparms']3738#39# Warnings and Exceptions40#4142class RankWarning(UserWarning):43"""Issued by chebfit when the design matrix is rank deficient."""44pass4546#47# Helper functions to convert inputs to 1-D arrays48#49def trimseq(seq):50"""Remove small Poly series coefficients.5152Parameters53----------54seq : sequence55Sequence of Poly series coefficients. This routine fails for56empty sequences.5758Returns59-------60series : sequence61Subsequence with trailing zeros removed. If the resulting sequence62would be empty, return the first element. The returned sequence may63or may not be a view.6465Notes66-----67Do not lose the type info if the sequence contains unknown objects.6869"""70if len(seq) == 0:71return seq72else:73for i in range(len(seq) - 1, -1, -1):74if seq[i] != 0:75break76return seq[:i+1]777879def as_series(alist, trim=True):80"""81Return argument as a list of 1-d arrays.8283The returned list contains array(s) of dtype double, complex double, or84object. A 1-d argument of shape ``(N,)`` is parsed into ``N`` arrays of85size one; a 2-d argument of shape ``(M,N)`` is parsed into ``M`` arrays86of size ``N`` (i.e., is "parsed by row"); and a higher dimensional array87raises a Value Error if it is not first reshaped into either a 1-d or 2-d88array.8990Parameters91----------92alist : array_like93A 1- or 2-d array_like94trim : boolean, optional95When True, trailing zeros are removed from the inputs.96When False, the inputs are passed through intact.9798Returns99-------100[a1, a2,...] : list of 1-D arrays101A copy of the input data as a list of 1-d arrays.102103Raises104------105ValueError106Raised when `as_series` cannot convert its input to 1-d arrays, or at107least one of the resulting arrays is empty.108109Examples110--------111>>> from numpy.polynomial import polyutils as pu112>>> a = np.arange(4)113>>> pu.as_series(a)114[array([0.]), array([1.]), array([2.]), array([3.])]115>>> b = np.arange(6).reshape((2,3))116>>> pu.as_series(b)117[array([0., 1., 2.]), array([3., 4., 5.])]118119>>> pu.as_series((1, np.arange(3), np.arange(2, dtype=np.float16)))120[array([1.]), array([0., 1., 2.]), array([0., 1.])]121122>>> pu.as_series([2, [1.1, 0.]])123[array([2.]), array([1.1])]124125>>> pu.as_series([2, [1.1, 0.]], trim=False)126[array([2.]), array([1.1, 0. ])]127128"""129arrays = [np.array(a, ndmin=1, copy=False) for a in alist]130if min([a.size for a in arrays]) == 0:131raise ValueError("Coefficient array is empty")132if any(a.ndim != 1 for a in arrays):133raise ValueError("Coefficient array is not 1-d")134if trim:135arrays = [trimseq(a) for a in arrays]136137if any(a.dtype == np.dtype(object) for a in arrays):138ret = []139for a in arrays:140if a.dtype != np.dtype(object):141tmp = np.empty(len(a), dtype=np.dtype(object))142tmp[:] = a[:]143ret.append(tmp)144else:145ret.append(a.copy())146else:147try:148dtype = np.common_type(*arrays)149except Exception as e:150raise ValueError("Coefficient arrays have no common type") from e151ret = [np.array(a, copy=True, dtype=dtype) for a in arrays]152return ret153154155def trimcoef(c, tol=0):156"""157Remove "small" "trailing" coefficients from a polynomial.158159"Small" means "small in absolute value" and is controlled by the160parameter `tol`; "trailing" means highest order coefficient(s), e.g., in161``[0, 1, 1, 0, 0]`` (which represents ``0 + x + x**2 + 0*x**3 + 0*x**4``)162both the 3-rd and 4-th order coefficients would be "trimmed."163164Parameters165----------166c : array_like1671-d array of coefficients, ordered from lowest order to highest.168tol : number, optional169Trailing (i.e., highest order) elements with absolute value less170than or equal to `tol` (default value is zero) are removed.171172Returns173-------174trimmed : ndarray1751-d array with trailing zeros removed. If the resulting series176would be empty, a series containing a single zero is returned.177178Raises179------180ValueError181If `tol` < 0182183See Also184--------185trimseq186187Examples188--------189>>> from numpy.polynomial import polyutils as pu190>>> pu.trimcoef((0,0,3,0,5,0,0))191array([0., 0., 3., 0., 5.])192>>> pu.trimcoef((0,0,1e-3,0,1e-5,0,0),1e-3) # item == tol is trimmed193array([0.])194>>> i = complex(0,1) # works for complex195>>> pu.trimcoef((3e-4,1e-3*(1-i),5e-4,2e-5*(1+i)), 1e-3)196array([0.0003+0.j , 0.001 -0.001j])197198"""199if tol < 0:200raise ValueError("tol must be non-negative")201202[c] = as_series([c])203[ind] = np.nonzero(np.abs(c) > tol)204if len(ind) == 0:205return c[:1]*0206else:207return c[:ind[-1] + 1].copy()208209def getdomain(x):210"""211Return a domain suitable for given abscissae.212213Find a domain suitable for a polynomial or Chebyshev series214defined at the values supplied.215216Parameters217----------218x : array_like2191-d array of abscissae whose domain will be determined.220221Returns222-------223domain : ndarray2241-d array containing two values. If the inputs are complex, then225the two returned points are the lower left and upper right corners226of the smallest rectangle (aligned with the axes) in the complex227plane containing the points `x`. If the inputs are real, then the228two points are the ends of the smallest interval containing the229points `x`.230231See Also232--------233mapparms, mapdomain234235Examples236--------237>>> from numpy.polynomial import polyutils as pu238>>> points = np.arange(4)**2 - 5; points239array([-5, -4, -1, 4])240>>> pu.getdomain(points)241array([-5., 4.])242>>> c = np.exp(complex(0,1)*np.pi*np.arange(12)/6) # unit circle243>>> pu.getdomain(c)244array([-1.-1.j, 1.+1.j])245246"""247[x] = as_series([x], trim=False)248if x.dtype.char in np.typecodes['Complex']:249rmin, rmax = x.real.min(), x.real.max()250imin, imax = x.imag.min(), x.imag.max()251return np.array((complex(rmin, imin), complex(rmax, imax)))252else:253return np.array((x.min(), x.max()))254255def mapparms(old, new):256"""257Linear map parameters between domains.258259Return the parameters of the linear map ``offset + scale*x`` that maps260`old` to `new` such that ``old[i] -> new[i]``, ``i = 0, 1``.261262Parameters263----------264old, new : array_like265Domains. Each domain must (successfully) convert to a 1-d array266containing precisely two values.267268Returns269-------270offset, scale : scalars271The map ``L(x) = offset + scale*x`` maps the first domain to the272second.273274See Also275--------276getdomain, mapdomain277278Notes279-----280Also works for complex numbers, and thus can be used to calculate the281parameters required to map any line in the complex plane to any other282line therein.283284Examples285--------286>>> from numpy.polynomial import polyutils as pu287>>> pu.mapparms((-1,1),(-1,1))288(0.0, 1.0)289>>> pu.mapparms((1,-1),(-1,1))290(-0.0, -1.0)291>>> i = complex(0,1)292>>> pu.mapparms((-i,-1),(1,i))293((1+1j), (1-0j))294295"""296oldlen = old[1] - old[0]297newlen = new[1] - new[0]298off = (old[1]*new[0] - old[0]*new[1])/oldlen299scl = newlen/oldlen300return off, scl301302def mapdomain(x, old, new):303"""304Apply linear map to input points.305306The linear map ``offset + scale*x`` that maps the domain `old` to307the domain `new` is applied to the points `x`.308309Parameters310----------311x : array_like312Points to be mapped. If `x` is a subtype of ndarray the subtype313will be preserved.314old, new : array_like315The two domains that determine the map. Each must (successfully)316convert to 1-d arrays containing precisely two values.317318Returns319-------320x_out : ndarray321Array of points of the same shape as `x`, after application of the322linear map between the two domains.323324See Also325--------326getdomain, mapparms327328Notes329-----330Effectively, this implements:331332.. math::333x\\_out = new[0] + m(x - old[0])334335where336337.. math::338m = \\frac{new[1]-new[0]}{old[1]-old[0]}339340Examples341--------342>>> from numpy.polynomial import polyutils as pu343>>> old_domain = (-1,1)344>>> new_domain = (0,2*np.pi)345>>> x = np.linspace(-1,1,6); x346array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ])347>>> x_out = pu.mapdomain(x, old_domain, new_domain); x_out348array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825, # may vary3496.28318531])350>>> x - pu.mapdomain(x_out, new_domain, old_domain)351array([0., 0., 0., 0., 0., 0.])352353Also works for complex numbers (and thus can be used to map any line in354the complex plane to any other line therein).355356>>> i = complex(0,1)357>>> old = (-1 - i, 1 + i)358>>> new = (-1 + i, 1 - i)359>>> z = np.linspace(old[0], old[1], 6); z360array([-1. -1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1. +1.j ])361>>> new_z = pu.mapdomain(z, old, new); new_z362array([-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 vary363364"""365x = np.asanyarray(x)366off, scl = mapparms(old, new)367return off + scl*x368369370def _nth_slice(i, ndim):371sl = [np.newaxis] * ndim372sl[i] = slice(None)373return tuple(sl)374375376def _vander_nd(vander_fs, points, degrees):377r"""378A generalization of the Vandermonde matrix for N dimensions379380The result is built by combining the results of 1d Vandermonde matrices,381382.. math::383W[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]}384385where386387.. math::388N &= \texttt{len(points)} = \texttt{len(degrees)} = \texttt{len(vander\_fs)} \\389M &= \texttt{points[k].ndim} \\390V_k &= \texttt{vander\_fs[k]} \\391x_k &= \texttt{points[k]} \\3920 \le j_k &\le \texttt{degrees[k]}393394Expanding the one-dimensional :math:`V_k` functions gives:395396.. math::397W[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])}398399where :math:`B_{k,m}` is the m'th basis of the polynomial construction used along400dimension :math:`k`. For a regular polynomial, :math:`B_{k, m}(x) = P_m(x) = x^m`.401402Parameters403----------404vander_fs : Sequence[function(array_like, int) -> ndarray]405The 1d vander function to use for each axis, such as ``polyvander``406points : Sequence[array_like]407Arrays of point coordinates, all of the same shape. The dtypes408will be converted to either float64 or complex128 depending on409whether any of the elements are complex. Scalars are converted to4101-D arrays.411This must be the same length as `vander_fs`.412degrees : Sequence[int]413The maximum degree (inclusive) to use for each axis.414This must be the same length as `vander_fs`.415416Returns417-------418vander_nd : ndarray419An array of shape ``points[0].shape + tuple(d + 1 for d in degrees)``.420"""421n_dims = len(vander_fs)422if n_dims != len(points):423raise ValueError(424f"Expected {n_dims} dimensions of sample points, got {len(points)}")425if n_dims != len(degrees):426raise ValueError(427f"Expected {n_dims} dimensions of degrees, got {len(degrees)}")428if n_dims == 0:429raise ValueError("Unable to guess a dtype or shape when no points are given")430431# convert to the same shape and type432points = tuple(np.array(tuple(points), copy=False) + 0.0)433434# produce the vandermonde matrix for each dimension, placing the last435# axis of each in an independent trailing axis of the output436vander_arrays = (437vander_fs[i](points[i], degrees[i])[(...,) + _nth_slice(i, n_dims)]438for i in range(n_dims)439)440441# we checked this wasn't empty already, so no `initial` needed442return functools.reduce(operator.mul, vander_arrays)443444445def _vander_nd_flat(vander_fs, points, degrees):446"""447Like `_vander_nd`, but flattens the last ``len(degrees)`` axes into a single axis448449Used to implement the public ``<type>vander<n>d`` functions.450"""451v = _vander_nd(vander_fs, points, degrees)452return v.reshape(v.shape[:-len(degrees)] + (-1,))453454455def _fromroots(line_f, mul_f, roots):456"""457Helper function used to implement the ``<type>fromroots`` functions.458459Parameters460----------461line_f : function(float, float) -> ndarray462The ``<type>line`` function, such as ``polyline``463mul_f : function(array_like, array_like) -> ndarray464The ``<type>mul`` function, such as ``polymul``465roots466See the ``<type>fromroots`` functions for more detail467"""468if len(roots) == 0:469return np.ones(1)470else:471[roots] = as_series([roots], trim=False)472roots.sort()473p = [line_f(-r, 1) for r in roots]474n = len(p)475while n > 1:476m, r = divmod(n, 2)477tmp = [mul_f(p[i], p[i+m]) for i in range(m)]478if r:479tmp[0] = mul_f(tmp[0], p[-1])480p = tmp481n = m482return p[0]483484485def _valnd(val_f, c, *args):486"""487Helper function used to implement the ``<type>val<n>d`` functions.488489Parameters490----------491val_f : function(array_like, array_like, tensor: bool) -> array_like492The ``<type>val`` function, such as ``polyval``493c, args494See the ``<type>val<n>d`` functions for more detail495"""496args = [np.asanyarray(a) for a in args]497shape0 = args[0].shape498if not all((a.shape == shape0 for a in args[1:])):499if len(args) == 3:500raise ValueError('x, y, z are incompatible')501elif len(args) == 2:502raise ValueError('x, y are incompatible')503else:504raise ValueError('ordinates are incompatible')505it = iter(args)506x0 = next(it)507508# use tensor on only the first509c = val_f(x0, c)510for xi in it:511c = val_f(xi, c, tensor=False)512return c513514515def _gridnd(val_f, c, *args):516"""517Helper function used to implement the ``<type>grid<n>d`` functions.518519Parameters520----------521val_f : function(array_like, array_like, tensor: bool) -> array_like522The ``<type>val`` function, such as ``polyval``523c, args524See the ``<type>grid<n>d`` functions for more detail525"""526for xi in args:527c = val_f(xi, c)528return c529530531def _div(mul_f, c1, c2):532"""533Helper function used to implement the ``<type>div`` functions.534535Implementation uses repeated subtraction of c2 multiplied by the nth basis.536For some polynomial types, a more efficient approach may be possible.537538Parameters539----------540mul_f : function(array_like, array_like) -> array_like541The ``<type>mul`` function, such as ``polymul``542c1, c2543See the ``<type>div`` functions for more detail544"""545# c1, c2 are trimmed copies546[c1, c2] = as_series([c1, c2])547if c2[-1] == 0:548raise ZeroDivisionError()549550lc1 = len(c1)551lc2 = len(c2)552if lc1 < lc2:553return c1[:1]*0, c1554elif lc2 == 1:555return c1/c2[-1], c1[:1]*0556else:557quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype)558rem = c1559for i in range(lc1 - lc2, - 1, -1):560p = mul_f([0]*i + [1], c2)561q = rem[-1]/p[-1]562rem = rem[:-1] - q*p[:-1]563quo[i] = q564return quo, trimseq(rem)565566567def _add(c1, c2):568""" Helper function used to implement the ``<type>add`` functions. """569# c1, c2 are trimmed copies570[c1, c2] = as_series([c1, c2])571if len(c1) > len(c2):572c1[:c2.size] += c2573ret = c1574else:575c2[:c1.size] += c1576ret = c2577return trimseq(ret)578579580def _sub(c1, c2):581""" Helper function used to implement the ``<type>sub`` functions. """582# c1, c2 are trimmed copies583[c1, c2] = as_series([c1, c2])584if len(c1) > len(c2):585c1[:c2.size] -= c2586ret = c1587else:588c2 = -c2589c2[:c1.size] += c1590ret = c2591return trimseq(ret)592593594def _fit(vander_f, x, y, deg, rcond=None, full=False, w=None):595"""596Helper function used to implement the ``<type>fit`` functions.597598Parameters599----------600vander_f : function(array_like, int) -> ndarray601The 1d vander function, such as ``polyvander``602c1, c2603See the ``<type>fit`` functions for more detail604"""605x = np.asarray(x) + 0.0606y = np.asarray(y) + 0.0607deg = np.asarray(deg)608609# check arguments.610if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0:611raise TypeError("deg must be an int or non-empty 1-D array of int")612if deg.min() < 0:613raise ValueError("expected deg >= 0")614if x.ndim != 1:615raise TypeError("expected 1D vector for x")616if x.size == 0:617raise TypeError("expected non-empty vector for x")618if y.ndim < 1 or y.ndim > 2:619raise TypeError("expected 1D or 2D array for y")620if len(x) != len(y):621raise TypeError("expected x and y to have same length")622623if deg.ndim == 0:624lmax = deg625order = lmax + 1626van = vander_f(x, lmax)627else:628deg = np.sort(deg)629lmax = deg[-1]630order = len(deg)631van = vander_f(x, lmax)[:, deg]632633# set up the least squares matrices in transposed form634lhs = van.T635rhs = y.T636if w is not None:637w = np.asarray(w) + 0.0638if w.ndim != 1:639raise TypeError("expected 1D vector for w")640if len(x) != len(w):641raise TypeError("expected x and w to have same length")642# apply weights. Don't use inplace operations as they643# can cause problems with NA.644lhs = lhs * w645rhs = rhs * w646647# set rcond648if rcond is None:649rcond = len(x)*np.finfo(x.dtype).eps650651# Determine the norms of the design matrix columns.652if issubclass(lhs.dtype.type, np.complexfloating):653scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1))654else:655scl = np.sqrt(np.square(lhs).sum(1))656scl[scl == 0] = 1657658# Solve the least squares problem.659c, resids, rank, s = np.linalg.lstsq(lhs.T/scl, rhs.T, rcond)660c = (c.T/scl).T661662# Expand c to include non-fitted coefficients which are set to zero663if deg.ndim > 0:664if c.ndim == 2:665cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype)666else:667cc = np.zeros(lmax+1, dtype=c.dtype)668cc[deg] = c669c = cc670671# warn on rank reduction672if rank != order and not full:673msg = "The fit may be poorly conditioned"674warnings.warn(msg, RankWarning, stacklevel=2)675676if full:677return c, [resids, rank, s, rcond]678else:679return c680681682def _pow(mul_f, c, pow, maxpower):683"""684Helper function used to implement the ``<type>pow`` functions.685686Parameters687----------688mul_f : function(array_like, array_like) -> ndarray689The ``<type>mul`` function, such as ``polymul``690c : array_like6911-D array of array of series coefficients692pow, maxpower693See the ``<type>pow`` functions for more detail694"""695# c is a trimmed copy696[c] = as_series([c])697power = int(pow)698if power != pow or power < 0:699raise ValueError("Power must be a non-negative integer.")700elif maxpower is not None and power > maxpower:701raise ValueError("Power is too large")702elif power == 0:703return np.array([1], dtype=c.dtype)704elif power == 1:705return c706else:707# This can be made more efficient by using powers of two708# in the usual way.709prd = c710for i in range(2, power + 1):711prd = mul_f(prd, c)712return prd713714715def _deprecate_as_int(x, desc):716"""717Like `operator.index`, but emits a deprecation warning when passed a float718719Parameters720----------721x : int-like, or float with integral value722Value to interpret as an integer723desc : str724description to include in any error message725726Raises727------728TypeError : if x is a non-integral float or non-numeric729DeprecationWarning : if x is an integral float730"""731try:732return operator.index(x)733except TypeError as e:734# Numpy 1.17.0, 2019-03-11735try:736ix = int(x)737except TypeError:738pass739else:740if ix == x:741warnings.warn(742f"In future, this will raise TypeError, as {desc} will "743"need to be an integer not just an integral float.",744DeprecationWarning,745stacklevel=3746)747return ix748749raise TypeError(f"{desc} must be an integer") from e750751752