def gradient(poly): """ Polynomial gradient operator. Args: poly (numpoly.ndpoly): Polynomial to differentiate. If polynomial vector is provided, the Jacobi-matrix is returned instead. Returns: Same as ``poly`` but with an extra first dimensions, one for each variable in ``poly.indeterminants``, filled with gradient values. Examples: >>> x, y = numpoly.symbols("x y") >>> poly = 5*x**5+4 >>> numpoly.gradient(poly) polynomial([25*x**4]) >>> poly = 4*x**3+2*y**2+3 >>> numpoly.gradient(poly) polynomial([12*x**2, 4*y]) >>> poly = numpoly.polynomial([1, x**3, x*y**2+1]) >>> numpoly.gradient(poly) polynomial([[0, 3*x**2, y**2], [0, 0, 2*x*y]]) """ poly = numpoly.aspolynomial(poly) polys = [diff(poly, diffvar)[numpy.newaxis] for diffvar in poly.names] return numpoly.concatenate(polys, axis=0)
def gradient(poly: PolyLike) -> ndpoly: """ Polynomial gradient operator. Args: poly: Polynomial to differentiate. If polynomial vector is provided, the Jacobi-matrix is returned instead. Returns: Same as ``poly`` but with an extra first dimensions, one for each variable in ``poly.indeterminants``, filled with gradient values. Examples: >>> q0 = numpoly.variable() >>> poly = 5*q0**5+4 >>> numpoly.gradient(poly) polynomial([25*q0**4]) >>> q0, q1 = numpoly.variable(2) >>> poly = 4*q0**3+2*q1**2+3 >>> numpoly.gradient(poly) polynomial([12*q0**2, 4*q1]) >>> poly = numpoly.polynomial([1, q0**3, q0*q1**2+1]) >>> numpoly.gradient(poly) polynomial([[0, 3*q0**2, q1**2], [0, 0, 2*q0*q1]]) """ poly = numpoly.aspolynomial(poly) polys = [ derivative(poly, diffvar)[numpy.newaxis] for diffvar in poly.names ] return numpoly.concatenate(polys, axis=0)
def decompose(poly: PolyLike) -> ndpoly: """ Decompose a polynomial to component form. In array missing values are padded with 0 to make decomposition compatible with ``chaospy.sum(output, 0)``. Args: poly: Polynomial to decompose. Returns: Decomposed polynomial with ``poly.shape==(M,)+output.shape``, where ``M`` is the number of components in `poly`. Examples: >>> q0 = numpoly.variable() >>> poly = numpoly.polynomial([q0**2-1, 2]) >>> poly polynomial([q0**2-1, 2]) >>> numpoly.decompose(poly) polynomial([[-1, 2], [q0**2, 0]]) >>> numpoly.sum(numpoly.decompose(poly), 0) polynomial([q0**2-1, 2]) """ poly = numpoly.aspolynomial(poly) return numpoly.concatenate([ numpoly.construct.polynomial_from_attributes( exponents=[expon], coefficients=[numpy.asarray(poly.values[key])], names=poly.indeterminants, retain_coefficients=True, retain_names=True, )[numpy.newaxis] for key, expon in zip(poly.keys, poly.exponents) ])
def decompose(poly): """ Decompose a polynomial to component form. In array missing values are padded with 0 to make decomposition compatible with ``chaospy.sum(ouput, 0)``. Args: poly (numpoly.ndpoly): Polynomial to decompose Returns: (numpoly.ndpoly): Decomposed polynomial with ``poly.shape==(M,)+output.shape`` where ``M`` is the number of components in `poly`. Examples: >>> x = numpoly.symbols() >>> poly = numpoly.polynomial([x**2-1, 2]) >>> poly polynomial([-1+q**2, 2]) >>> numpoly.decompose(poly) polynomial([[-1, 2], [q**2, 0]]) >>> numpoly.sum(numpoly.decompose(poly), 0) polynomial([-1+q**2, 2]) """ return numpoly.concatenate([ numpoly.construct.polynomial_from_attributes( exponents=[expon], coefficients=[poly[key]], names=poly.indeterminants, clean=False, )[numpy.newaxis] for key, expon in zip(poly.keys, poly.exponents) ])
def compose_polynomial_array( arrays: Sequence[PolyLike], dtype: Optional[numpy.typing.DTypeLike] = None, allocation: Optional[int] = None, ) -> ndpoly: """ Compose polynomial from array of arrays of polynomials. Backend for `numpoly.polynomial` when input is undetermined. Args: arrays: Input to be converted to a `numpoly.ndpoly` polynomial type. dtype: Data type used for the polynomial coefficients. allocation: The maximum number of polynomial exponents. If omitted, use length of exponents for allocation. Return: Polynomial based on input `arrays`. """ oarrays = numpy.array(arrays, dtype=object) shape = oarrays.shape if not oarrays.size: return numpoly.ndpoly(shape=shape, dtype=dtype) if len(oarrays.shape) > 1: return numpoly.concatenate([ numpoly.expand_dims(numpoly.aspolynomial(array, dtype=dtype), axis=0) for array in arrays ], axis=0) oarrays = oarrays.flatten() indices = numpy.array( [isinstance(array, numpoly.ndpoly) for array in oarrays]) oarrays[indices] = numpoly.align_indeterminants(*oarrays[indices]) names = oarrays[indices][0] if numpy.any(indices) else None oarrays = oarrays.tolist() dtypes: List[numpy.typing.DTypeLike] = [] keys: Set[Tuple[int, ...]] = {(0, )} for array in oarrays: if isinstance(array, numpoly.ndpoly): dtypes.append(array.dtype) keys = keys.union({ tuple(int(k) for k in key) for key in array.exponents.tolist() }) elif isinstance(array, (numpy.generic, numpy.ndarray)): dtypes.append(array.dtype) else: dtypes.append(type(array)) if dtype is None: dtype = numpy.find_common_type(dtypes, []) length = max(1, max([len(key) for key in keys])) collection = {} for idx, array in enumerate(oarrays): if isinstance(array, numpoly.ndpoly): for key, value in zip(array.exponents, array.coefficients): key = tuple(key) + (0, ) * (length - len(key)) if key not in collection: collection[key] = numpy.zeros(len(oarrays), dtype=dtype) collection[key][idx] = value else: key = (0, ) * length if key not in collection: collection[key] = numpy.zeros(len(oarrays), dtype=dtype) collection[key][idx] = array exponents = sorted(collection) coefficients = numpy.array([collection[key] for key in exponents]) coefficients = coefficients.reshape(-1, *shape) return numpoly.ndpoly.from_attributes( exponents=exponents, coefficients=list(coefficients), names=names, allocation=allocation, )
def compose_polynomial_array( arrays, dtype=None, ): """ Compose polynomial from array of arrays of polynomials. Backend for `numpoly.polynomial` when input is undetermined. Args: arrays (Any): Input to be converted to a `numpoly.ndpoly` polynomial type. dtype (Optional[numpy.dtype]): Data type used for the polynomial coefficients. Return: (numpoly.ndpoly): Polynomial based on input `arrays`. """ arrays_ = numpy.array(arrays, dtype=object) shape = arrays_.shape if not arrays_.size: return numpoly.ndpoly(shape=shape, dtype=dtype) if len(arrays_.shape) > 1: return numpoly.concatenate([ numpoly.aspolynomial(array, dtype)[numpy.newaxis] for array in arrays ], axis=0) arrays = arrays_.flatten() indices = numpy.array( [isinstance(array, numpoly.ndpoly) for array in arrays]) arrays[indices] = numpoly.align_indeterminants(*arrays[indices]) names = arrays[indices][0] if numpy.any(indices) else None arrays = arrays.tolist() dtypes = [] keys = {(0, )} for array in arrays: if isinstance(array, numpoly.ndpoly): dtypes.append(array.dtype) keys = keys.union([tuple(key) for key in array.exponents.tolist()]) elif isinstance(array, (numpy.generic, numpy.ndarray)): dtypes.append(array.dtype) else: dtypes.append(type(array)) if dtype is None: dtype = numpy.find_common_type(dtypes, []) length = max([len(key) for key in keys]) collection = {} for idx, array in enumerate(arrays): if isinstance(array, numpoly.ndpoly): for key, value in zip(array.exponents, array.coefficients): key = tuple(key) + (0, ) * (length - len(key)) if key not in collection: collection[key] = numpy.zeros(len(arrays), dtype=dtype) collection[key][idx] = value else: key = (0, ) * length if key not in collection: collection[key] = numpy.zeros(len(arrays), dtype=dtype) collection[key][idx] = array exponents = sorted(collection) coefficients = numpy.array([collection[key] for key in exponents]) coefficients = coefficients.reshape(-1, *shape) exponents, coefficients, names = postprocess_attributes( exponents, coefficients, names) return numpoly.ndpoly.from_attributes( exponents=exponents, coefficients=coefficients, names=names, )
def power(x1, x2, **kwargs): """ First array elements raised to powers from second array, element-wise. Raise each base in `x1` to the positionally-corresponding power in `x2`. `x1` and `x2` must be broadcastable to the same shape. Note that an integer type raised to a negative integer power will raise a ValueError. Args: x1 (numpoly.ndpoly): The bases. x2 (numpoly.ndpoly): The exponents. If ``x1.shape != x2.shape``, they must be broadcastable to a common shape (which becomes the shape of the output). out (Optional[numpy.ndarray]): 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 (Optional[numpy.ndarray]): 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: (numpoly.ndpoly): The bases in `x1` raised to the exponents in `x2`. This is a scalar if both `x1` and `x2` are scalars. Examples: >>> x = numpoly.symbols("x") >>> (x+1)**3 polynomial(1+3*x+3*x**2+x**3) >>> poly = numpoly.symbols("x y") >>> (poly+1)**[1, 2] polynomial([1+x, 1+2*y+y**2]) """ x1 = numpoly.aspolynomial(x1) x2 = numpoly.aspolynomial(x2).tonumpy().astype(int) if not x2.shape: out = numpoly.ndpoly.from_attributes( [(0, )], [numpy.ones(x1.shape, dtype=x1._dtype)], x1.names[:1]) for _ in range(x2.item()): out = numpoly.multiply(out, x1, **kwargs) elif x1.shape: if x2.shape[-1] == 1: if x1.shape[-1] == 1: out = numpoly.power(x1.T[0].T, x2.T[0].T).T[numpy.newaxis].T else: out = numpoly.concatenate( [power(x, x2.T[0])[numpy.newaxis] for x in x1.T], axis=0).T elif x1.shape[-1] == 1: out = numpoly.concatenate( [power(x1.T[0].T, x.T).T[numpy.newaxis] for x in x2.T], axis=0).T else: out = numpoly.concatenate([ power(x1_, x2_).T[numpy.newaxis] for x1_, x2_ in zip(x1.T, x2.T) ], axis=0).T else: out = numpoly.concatenate( [power(x1, x.T).T[numpy.newaxis] for x in x2.T], axis=0).T return out