def inner(a: PolyLike, b: PolyLike) -> ndpoly: """ Inner product of two arrays. Ordinary inner product of vectors for 1-D arrays (without complex conjugation), in higher dimensions a sum product over the last axes. """ a, b = numpoly.align_exponents(a, b) return numpoly.sum(numpoly.multiply(a, b), axis=-1)
def ediff1d( ary: PolyLike, to_end: Optional[PolyLike] = None, to_begin: Optional[PolyLike] = None, ) -> ndpoly: """ Difference between consecutive elements of an array. Args: ary: If necessary, will be flattened before the differences are taken. to_end: Polynomial(s) to append at the end of the returned differences. to_begin: Polynomial(s) to prepend at the beginning of the returned differences. Returns: The differences. Loosely, this is ``ary.flat[1:] - ary.flat[:-1]``. Examples: >>> poly = numpoly.monomial(4) >>> poly polynomial([1, q0, q0**2, q0**3]) >>> numpoly.ediff1d(poly) polynomial([q0-1, q0**2-q0, q0**3-q0**2]) >>> q0, q1 = numpoly.variable(2) >>> numpoly.ediff1d(poly, to_begin=q0, to_end=[1, q1]) polynomial([q0, q0-1, q0**2-q0, q0**3-q0**2, 1, q1]) """ ary = numpoly.aspolynomial(ary).ravel() arys_ = [ary[1:] - ary[:-1]] if to_end is not None: arys_.append(numpoly.aspolynomial(to_end).ravel()) if to_begin is not None: arys_.insert(0, numpoly.aspolynomial(to_begin).ravel()) arys = tuple(numpoly.aspolynomial(ary) for ary in arys_) if len(arys) > 1: arys = numpoly.align_exponents(*arys) out = numpoly.ndpoly( exponents=arys[0].exponents, shape=(sum([ary.size for ary in arys]), ), names=arys[0].names, dtype=ary[0].dtype, ) idx = 0 for ary in arys: for key in ary.keys: out.values[key][idx:idx + ary.size] = ary.values[key] idx += ary.size return out
def stack( arrays: Sequence[PolyLike], axis: int = 0, out: Optional[ndpoly] = None, ) -> ndpoly: """ Join a sequence of arrays along a new axis. The ``axis`` parameter specifies the index of the new axis in the dimensions of the result. For example, if ``axis=0`` it will be the first dimension and if ``axis=-1`` it will be the last dimension. Args: arrays: Each array must have the same shape. axis: The axis in the result array along which the input arrays are stacked. out: If provided, the destination to place the result. The shape must be correct, matching that of what stack would have returned if no out argument were specified. Returns: The stacked array has one more dimension than the input arrays. Examples: >>> poly = numpoly.variable(3) >>> const = numpoly.polynomial([1, 2, 3]) >>> numpoly.stack([poly, const]) polynomial([[q0, q1, q2], [1, 2, 3]]) >>> numpoly.stack([poly, const], axis=-1) polynomial([[q0, 1], [q1, 2], [q2, 3]]) """ arrays = numpoly.align_exponents(*arrays) if out is None: coefficients = [numpy.stack( [array.values[key] for array in arrays], axis=axis) for key in arrays[0].keys] out = numpoly.polynomial_from_attributes( exponents=arrays[0].exponents, coefficients=coefficients, names=arrays[0].names, dtype=coefficients[0].dtype, ) else: for key in out.keys: if key in arrays[0].keys: numpy.stack([array.values[key] for array in arrays], out=out.values[key], axis=axis) return out
def concatenate( arrays: Sequence[PolyLike], axis: int = 0, out: Optional[ndpoly] = None, ) -> ndpoly: """ Join a sequence of arrays along an existing axis. Args: arrays: The arrays must have the same shape, except in the dimension corresponding to `axis` (the first, by default). axis: The axis along which the arrays will be joined. If axis is None, arrays are flattened before use. Default is 0. out: If provided, the destination to place the result. The shape must be correct, matching that of what concatenate would have returned if no out argument were specified. Returns: The concatenated array. Examples: >>> const = numpy.array([[1, 2], [3, 4]]) >>> poly = numpoly.variable(2).reshape(1, 2) >>> numpoly.concatenate((const, poly), axis=0) polynomial([[1, 2], [3, 4], [q0, q1]]) >>> numpoly.concatenate((const, poly.T), axis=1) polynomial([[1, 2, q0], [3, 4, q1]]) >>> numpoly.concatenate((const, poly), axis=None) polynomial([1, 2, 3, 4, q0, q1]) """ arrays = numpoly.align_exponents(*arrays) if out is None: coefficients = [numpy.concatenate( [array.values[key] for array in arrays], axis=axis) for key in arrays[0].keys] out = numpoly.polynomial_from_attributes( exponents=arrays[0].exponents, coefficients=coefficients, names=arrays[0].names, dtype=coefficients[0].dtype, ) else: for key in out.keys: if key in arrays[0].keys: numpy.concatenate([array.values[key] for array in arrays], out=out.values[key], axis=axis) return out
def dstack(tup: Sequence[PolyLike]) -> ndpoly: """ Stack arrays in sequence depth wise (along third axis). This is equivalent to concatenation along the third axis after 2-D arrays of shape `(M,N)` have been reshaped to `(M,N,1)` and 1-D arrays of shape `(N,)` have been reshaped to `(1,N,1)`. Rebuilds arrays divided by `dsplit`. This function makes most sense for arrays with up to 3 dimensions. For instance, for pixel-data with a height (first axis), width (second axis), and r/g/b channels (third axis). The functions `concatenate`, `stack` and `block` provide more general stacking and concatenation operations. Args: tup: The arrays must have the same shape along all but the third axis. 1-D or 2-D arrays must have the same shape. Returns: The array formed by stacking the given arrays, will be at least 3-D. Examples: >>> poly1 = numpoly.variable(3) >>> const1 = numpoly.polynomial([1, 2, 3]) >>> numpoly.dstack([poly1, const1]) polynomial([[[q0, 1], [q1, 2], [q2, 3]]]) >>> const2 = numpoly.polynomial([[1], [2], [3]]) >>> poly2 = poly1.reshape(3, 1) >>> numpoly.dstack([const2, poly2]) polynomial([[[1, q0]], <BLANKLINE> [[2, q1]], <BLANKLINE> [[3, q2]]]) """ arrays = numpoly.align_exponents(*tup) coefficients = [numpy.dstack([array.values[key] for array in arrays]) for key in arrays[0].keys] return numpoly.polynomial_from_attributes( exponents=arrays[0].exponents, coefficients=coefficients, names=arrays[0].names, dtype=coefficients[0].dtype, )
def roots(poly: PolyLike) -> numpy.ndarray: """ Return the roots of a polynomial. Assumes the polynomial has a single dimension. Args: poly: Polynomial to take roots of, or if constant, the coefficients of said polynomial. This to be compatible with :func:`numpy.roots`. Returns: An array containing the roots of the polynomial. Raises: ValueError: When `poly` cannot be converted to a rank-1 polynomial. Notes: The algorithm relies on computing the eigenvalues of the companion matrix [1]_. .. [1] R. A. Horn & C. R. Johnson, *Matrix Analysis*. Cambridge, UK: Cambridge University Press, 1999, pp. 146-7. Examples: >>> q0 = numpoly.variable() >>> poly = 3.2*q0**2+2*q0+1 >>> numpoly.roots(poly) array([-0.3125+0.46351241j, -0.3125-0.46351241j]) >>> numpoly.roots([3.2, 2, 1]) array([-0.3125+0.46351241j, -0.3125-0.46351241j]) """ # backwards compatibility poly = numpoly.aspolynomial(poly) if poly.isconstant(): return numpy.roots(poly.tonumpy()) # only rank-1 if len(poly.names) > 1: raise ValueError("polynomial is not of rank 1.") # align exponents to include all coefficients filled_basis = poly.indeterminants**numpy.arange( numpoly.lead_exponent(poly), dtype=int) _, poly = numpoly.align_exponents(filled_basis, poly) # pass coefficients to numpy return numpy.roots(poly.coefficients[::-1])
def hstack(tup: Sequence[PolyLike]) -> ndpoly: """ Stack arrays in sequence horizontally (column wise). This is equivalent to concatenation along the second axis, except for 1-D arrays where it concatenates along the first axis. Rebuilds arrays divided by `hsplit`. This function makes most sense for arrays with up to 3 dimensions. For instance, for pixel-data with a height (first axis), width (second axis), and r/g/b channels (third axis). The functions `concatenate`, `stack` and `block` provide more general stacking and concatenation operations. Args: tup: The arrays must have the same shape along all but the second axis, except 1-D arrays which can be any length. Returns: The array formed by stacking the given arrays. Examples: >>> poly1 = numpoly.variable(3) >>> const1 = numpoly.polynomial([1, 2, 3]) >>> numpoly.hstack([poly1, const1]) polynomial([q0, q1, q2, 1, 2, 3]) >>> const2 = numpoly.polynomial([[1], [2], [3]]) >>> poly2 = poly1.reshape(3, 1) >>> numpoly.hstack([const2, poly2]) polynomial([[1, q0], [2, q1], [3, q2]]) """ arrays = numpoly.align_exponents(*tup) coefficients = [ numpy.hstack([array.values[key] for array in arrays]) for key in arrays[0].keys ] return numpoly.polynomial_from_attributes( exponents=arrays[0].exponents, coefficients=coefficients, names=arrays[0].names, dtype=coefficients[0].dtype, )
def dstack(tup): """ Stack arrays in sequence depth wise (along third axis). This is equivalent to concatenation along the third axis after 2-D arrays of shape `(M,N)` have been reshaped to `(M,N,1)` and 1-D arrays of shape `(N,)` have been reshaped to `(1,N,1)`. Rebuilds arrays divided by `dsplit`. This function makes most sense for arrays with up to 3 dimensions. For instance, for pixel-data with a height (first axis), width (second axis), and r/g/b channels (third axis). The functions `concatenate`, `stack` and `block` provide more general stacking and concatenation operations. Args: tup (Sequence[numpoly.ndpoly]): The arrays must have the same shape along all but the third axis. 1-D or 2-D arrays must have the same shape. Returns: (numpoly.ndpoly): The array formed by stacking the given arrays, will be at least 3-D. Examples: >>> a = numpoly.symbols("x y z") >>> b = numpoly.polynomial([1, 2, 3]) >>> numpoly.dstack([a, b]) polynomial([[[x, 1], [y, 2], [z, 3]]]) >>> c = numpoly.polynomial([[1], [2], [3]]) >>> d = a.reshape(3, 1) >>> numpoly.dstack([c, d]) polynomial([[[1, x]], <BLANKLINE> [[2, y]], <BLANKLINE> [[3, z]]]) """ arrays = numpoly.align_exponents(*tup) arrays = numpoly.align_dtype(*arrays) result = numpy.dstack([array.values for array in arrays]) return numpoly.aspolynomial(result, names=arrays[0].indeterminants)
def outer( a: PolyLike, b: PolyLike, out: Optional[ndpoly] = None, ) -> ndpoly: """ Compute the outer product of two vectors. Given two vectors, ``a = [a0, a1, ..., aM]`` and ``b = [b0, b1, ..., bN]``, the outer product is:: [[a0*b0 a0*b1 ... a0*bN ] [a1*b0 . [ ... . [aM*b0 aM*bN ]] Args: a: First input vector. Input is flattened if not already 1-dimensional. b: Second input vector. Input is flattened if not already 1-dimensional. out: A location where the result is stored. Returns: ``out[i, j] = a[i] * b[j]`` Examples: >>> poly = numpoly.variable(3) >>> const = numpy.arange(5) >>> numpoly.outer(poly, const) polynomial([[0, q0, 2*q0, 3*q0, 4*q0], [0, q1, 2*q1, 3*q1, 4*q1], [0, q2, 2*q2, 3*q2, 4*q2]]) """ a, b = numpoly.align_exponents(a, b) a = a.ravel()[:, numpy.newaxis] b = b.ravel()[numpy.newaxis, :] return numpoly.multiply(a, b, out=out)
def stack(arrays, axis=0, out=None): """ Join a sequence of arrays along a new axis. The ``axis`` parameter specifies the index of the new axis in the dimensions of the result. For example, if ``axis=0`` it will be the first dimension and if ``axis=-1`` it will be the last dimension. Args: arrays (Sequence[numpoly.ndpoly]): Each array must have the same shape. axis (Optional[int]): The axis in the result array along which the input arrays are stacked. out (Optional[numpy.ndarray]): If provided, the destination to place the result. The shape must be correct, matching that of what stack would have returned if no out argument were specified. Returns: (numpoly.ndpoly): The stacked array has one more dimension than the input arrays. Examples: >>> a = numpoly.symbols("x y z") >>> b = numpoly.polynomial([1, 2, 3]) >>> numpoly.stack([a, b]) polynomial([[x, y, z], [1, 2, 3]]) >>> numpoly.stack([a, b], axis=-1) polynomial([[x, 1], [y, 2], [z, 3]]) """ arrays = numpoly.align_exponents(*arrays) arrays = numpoly.align_dtype(*arrays) result = numpy.stack( [array.values for array in arrays], axis=axis, out=out) return numpoly.aspolynomial(result, names=arrays[0].indeterminants)
def concatenate(arrays, axis=0, out=None): """ Join a sequence of arrays along an existing axis. Args: arrays (Iterable[numpoly.ndpoly]): The arrays must have the same shape, except in the dimension corresponding to `axis` (the first, by default). axis (Optional[int]): The axis along which the arrays will be joined. If axis is None, arrays are flattened before use. Default is 0. out (Optional[numpy.ndarray]): If provided, the destination to place the result. The shape must be correct, matching that of what concatenate would have returned if no out argument were specified. Returns: (numpoly.ndpoly): The concatenated array. Examples: >>> a = numpy.array([[1, 2], [3, 4]]) >>> b = numpoly.symbols("x y").reshape(1, 2) >>> numpoly.concatenate((a, b), axis=0) polynomial([[1, 2], [3, 4], [x, y]]) >>> numpoly.concatenate((a, b.T), axis=1) polynomial([[1, 2, x], [3, 4, y]]) >>> numpoly.concatenate((a, b), axis=None) polynomial([1, 2, 3, 4, x, y]) """ arrays = numpoly.align_exponents(*arrays) arrays = numpoly.align_dtype(*arrays) result = numpy.concatenate( [array.values for array in arrays], axis=axis, out=out) return numpoly.aspolynomial(result, names=arrays[0].indeterminants)
def hstack(tup): """ Stack arrays in sequence horizontally (column wise). This is equivalent to concatenation along the second axis, except for 1-D arrays where it concatenates along the first axis. Rebuilds arrays divided by `hsplit`. This function makes most sense for arrays with up to 3 dimensions. For instance, for pixel-data with a height (first axis), width (second axis), and r/g/b channels (third axis). The functions `concatenate`, `stack` and `block` provide more general stacking and concatenation operations. Args: tup (Sequence[numpoly.ndpoly]): The arrays must have the same shape along all but the second axis, except 1-D arrays which can be any length. Returns: (numpoly.ndpoly): The array formed by stacking the given arrays. Examples: >>> a = numpoly.symbols("x y z") >>> b = numpoly.polynomial([1, 2, 3]) >>> numpoly.hstack([a, b]) polynomial([x, y, z, 1, 2, 3]) >>> c = numpoly.polynomial([[1], [2], [3]]) >>> d = a.reshape(3, 1) >>> numpoly.hstack([c, d]) polynomial([[1, x], [2, y], [3, z]]) """ arrays = numpoly.align_exponents(*tup) arrays = numpoly.align_dtype(*arrays) result = numpy.hstack([array.values for array in arrays]) return numpoly.aspolynomial(result, names=arrays[0].indeterminants)
def outer(a, b, out=None): """ Compute the outer product of two vectors. Given two vectors, ``a = [a0, a1, ..., aM]`` and ``b = [b0, b1, ..., bN]``, the outer product is:: [[a0*b0 a0*b1 ... a0*bN ] [a1*b0 . [ ... . [aM*b0 aM*bN ]] Args: a (numpoly.ndpoly): First input vector. Input is flattened if not already 1-dimensional. b (numpoly.ndpoly): Second input vector. Input is flattened if not already 1-dimensional. out (numpy.ndarray): A location where the result is stored. Returns: (numpoly.ndpoly): ``out[i, j] = a[i] * b[j]`` Examples: >>> numpoly.outer(numpoly.symbols("x y z"), numpy.arange(5)) polynomial([[0, x, 2*x, 3*x, 4*x], [0, y, 2*y, 3*y, 4*y], [0, z, 2*z, 3*z, 4*z]]) """ a, b = numpoly.align_exponents(a, b) a = a.flatten()[:, numpy.newaxis] b = b.flatten()[numpy.newaxis, :] return numpoly.multiply(a, b, out=out)
def not_equal( x1: PolyLike, x2: PolyLike, out: Optional[numpy.ndarray] = None, where: numpy.typing.ArrayLike = True, **kwargs: Any, ) -> numpy.ndarray: """ Return (x1 != x2) element-wise. Args: x1, x2: Input arrays. If ``x1.shape != x2.shape``, they must be broadcastable to a common shape (which becomes the shape of the output). out: A location into which the result is stored. If provided, it must have a shape that the inputs broadcast to. If not provided or `None`, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. where: This condition is broadcast over the input. At locations where the condition is True, the `out` array will be set to the ufunc result. Elsewhere, the `out` array will retain its original value. Note that if an uninitialized `out` array is created via the default ``out=None``, locations within it where the condition is False will remain uninitialized. kwargs: Keyword args passed to numpy.ufunc. Returns: Output array, element-wise comparison of `x1` and `x2`. Typically of type bool, unless ``dtype=object`` is passed. Examples: >>> q0, q1 = numpoly.variable(2) >>> numpoly.not_equal([q0, q0], [q0, q1]) array([False, True]) >>> numpoly.not_equal([q0, q0], [[q0, q1], [q1, q0]]) array([[False, True], [ True, False]]) """ x1, x2 = numpoly.align_exponents(x1, x2) if not x1.flags["OWNDATA"]: x1 = numpoly.polynomial(x1) if not x2.flags["OWNDATA"]: x2 = numpoly.polynomial(x2) # x1, x2 = numpoly.align_polynomials(x1, x2) where = numpy.asarray(where) for key in x1.keys: tmp = numpy.not_equal(x1.values[key], x2.values[key], where=where, **kwargs) if out is None: out = tmp else: out |= tmp return numpy.asarray(out)
def diff( a: PolyLike, n: int = 1, axis: int = -1, prepend: Optional[PolyLike] = None, append: Optional[PolyLike] = None, ) -> ndpoly: """ Calculate the n-th discrete difference along the given axis. The first difference is given by ``out[i] = a[i+1] - a[i]`` along the given axis, higher differences are calculated by using `diff` recursively. Args: a: Input array n: The number of times values are "differenced". If zero, the input is returned as-is. axis: The axis along which the difference is taken, default is the last axis. prepend, append: Values to prepend or append to `a` along axis prior to performing the difference. Scalar values are expanded to arrays with length 1 in the direction of axis and the shape of the input array in along all other axes. Otherwise the dimension and shape must match `a` except along axis. Returns: The n-th differences. The shape of the output is the same as `a` except along `axis` where the dimension is smaller by `n`. The type of the output is the same as the type of the difference between any two elements of `a`. This is the same as the type of `a` in most cases. Examples: >>> q0, q1 = numpoly.variable(2) >>> poly = numpoly.polynomial([1, q0, q1, q0**2, q1-1]) >>> numpoly.diff(poly) polynomial([q0-1, q1-q0, q0**2-q1, -q0**2+q1-1]) >>> numpoly.diff(poly, n=2) polynomial([q1-2*q0+1, q0**2-2*q1+q0, -2*q0**2+2*q1-1]) >>> poly = numpoly.polynomial([[q0, 1], [2, q1]]) >>> numpoly.diff(poly) polynomial([[-q0+1], [q1-2]]) >>> numpoly.diff(poly, prepend=7, append=q1) polynomial([[q0-7, -q0+1, q1-1], [-5, q1-2, 0]]) """ if append is not None: if prepend is not None: a, append, prepend = numpoly.align_exponents(a, append, prepend) else: a, append = numpoly.align_exponents(a, append) elif prepend is not None: a, prepend = numpoly.align_exponents(a, prepend) else: a = numpoly.aspolynomial(a) out = None for key in a.keys: kwargs = {} if append is not None: kwargs["append"] = append.values[key] if prepend is not None: kwargs["prepend"] = prepend.values[key] tmp = numpy.diff(a.values[key], n=n, axis=axis, **kwargs) if out is None: out = numpoly.ndpoly( exponents=a.exponents, shape=tmp.shape, names=a.indeterminants, dtype=tmp.dtype, ) out.values[key] = tmp assert out is not None return numpoly.clean_attributes(out)
def E_cond(poly, freeze, dist, **kws): """ Conditional expected value operator. 1st order statistics of a polynomial on a given probability space conditioned on some of the variables. Args: poly (numpoly.ndpoly): Polynomial to find conditional expected value on. freeze (numpy.ndarray): Boolean values defining the conditional variables. True values implies that the value is conditioned on, e.g. frozen during the expected value calculation. dist (Distribution) : The distributions of the input used in ``poly``. Returns: (numpoly.ndpoly) : Same as ``poly``, but with the variables not tagged in ``frozen`` integrated away. Examples: >>> q0, q1 = chaospy.variable(2) >>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1]) >>> poly polynomial([1, q0, q1, 10*q0*q1-1]) >>> dist = chaospy.J(chaospy.Gamma(1, 1), chaospy.Normal(0, 2)) >>> chaospy.E_cond(poly, [1, 0], dist) polynomial([1.0, q0, 0.0, -1.0]) >>> chaospy.E_cond(poly, [0, 1], dist) polynomial([1.0, 1.0, q1, 10.0*q1-1.0]) >>> chaospy.E_cond(poly, [1, 1], dist) polynomial([1, q0, q1, 10*q0*q1-1]) >>> chaospy.E_cond(poly, [0, 0], dist) polynomial([1.0, 1.0, 0.0, -1.0]) """ poly = numpoly.set_dimensions(poly, len(dist)) if not poly.isconstant: return poly.tonumpy() assert not dist.stochastic_dependent, dist freeze = numpoly.polynomial(freeze) if freeze.isconstant(): freeze = freeze.tonumpy().astype(bool) else: poly, freeze = numpoly.align_exponents(poly, freeze) freeze = numpy.isin(poly.keys, freeze.keys) # decompose into frozen and unfrozen part poly = numpoly.decompose(poly) unfrozen = poly(**{("q%d" % idx): 1 for idx, keep in enumerate(freeze) if keep}) frozen = poly(**{("q%d" % idx): 1 for idx, keep in enumerate(freeze) if not keep}) # if no unfrozen, poly will return numpy.ndarray instead of numpoly.ndpoly if not isinstance(unfrozen, numpoly.ndpoly): return numpoly.sum(frozen, 0) # Remove frozen coefficients, such that poly == sum(frozen*unfrozen) holds for key in unfrozen.keys: unfrozen[key] = unfrozen[key] != 0 return numpoly.sum(frozen * expected.E(unfrozen, dist), 0)