def __getitem__(self, index: Any) -> "ndpoly": """ Get array item or slice. Args: index: The index to extract. Returns: Polynomial array element. Examples: >>> q0, q1 = numpoly.variable(2) >>> poly = numpoly.polynomial([[1-4*q0, q0**2], [q1-3, q0*q1*q1]]) >>> poly polynomial([[-4*q0+1, q0**2], [q1-3, q0*q1**2]]) >>> poly[0] polynomial([-4*q0+1, q0**2]) >>> poly[:, 1] polynomial([q0**2, q0*q1**2]) """ return numpoly.polynomial_from_attributes( exponents=self.exponents, coefficients=[coeff[index] for coeff in self.coefficients], names=self.names, )
def __iter__(self) -> Iterator["ndpoly"]: """Iterate polynomial array.""" coefficients = numpy.array(list(self.coefficients)) return iter(numpoly.polynomial_from_attributes( exponents=self.exponents, coefficients=coefficients[:, idx], names=self.names, ) for idx in range(len(self)))
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 astype(self, dtype: Any, **kwargs: Any) -> "ndpoly": # type: ignore """Wrap ndarray.astype.""" coefficients = [coefficient.astype(dtype, **kwargs) for coefficient in self.coefficients] return numpoly.polynomial_from_attributes( exponents=self.exponents, coefficients=coefficients, names=self.names, allocation=self.allocation, dtype=dtype, )
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 where(condition: numpy.typing.ArrayLike, *args: PolyLike) -> ndpoly: """ Return elements chosen from `x` or `y` depending on `condition`. .. note:: When only `condition` is provided, this function is a shorthand for ``np.asarray(condition).nonzero()``. Using `nonzero` directly should be preferred, as it behaves correctly for subclasses. The rest of this documentation covers only the case where all three arguments a re provided. Args: condition: Where True, yield `x`, otherwise yield `y`. x: Values from which to choose. `x`, `y` and `condition` need to be broadcastable to some shape. Returns: An array with elements from `x` where `condition` is True, and elements from `y` elsewhere. Examples: >>> poly = numpoly.variable()*numpy.arange(4) >>> poly polynomial([0, q0, 2*q0, 3*q0]) >>> numpoly.where([1, 0, 1, 0], 7, 2*poly) polynomial([7, 2*q0, 7, 6*q0]) >>> numpoly.where(poly, 2*poly, 4) polynomial([4, 2*q0, 4*q0, 6*q0]) >>> numpoly.where(poly) (array([1, 2, 3]),) """ if isinstance(condition, numpoly.ndpoly): condition = numpy.any(numpy.asarray(condition.coefficients), 0).astype(bool) if not args: return numpy.where(condition) poly1, poly2 = numpoly.align_polynomials(*args) coefficients = [ numpy.where(condition, x1, x2) for x1, x2 in zip(poly1.coefficients, poly2.coefficients) ] dtype = numpy.result_type(poly1.dtype, poly2.dtype) return numpoly.polynomial_from_attributes( exponents=poly1.exponents, coefficients=coefficients, names=poly1.names, dtype=dtype, )
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 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 indeterminants(self) -> "ndpoly": """ Polynomial indeterminants. Secondary polynomial only consisting of an array of simple independent variables found in the polynomial array. Examples: >>> q0, q1 = numpoly.variable(2) >>> poly = numpoly.polynomial([2*q0**4, -3*q1**2+14]) >>> poly polynomial([2*q0**4, -3*q1**2+14]) >>> poly.indeterminants polynomial([q0, q1]) """ return numpoly.polynomial_from_attributes( exponents=numpy.eye(len(self.names), dtype=int), coefficients=numpy.eye(len(self.names), dtype=int), names=self.names, )
def from_attributes( exponents: numpy.typing.ArrayLike, coefficients: Sequence[numpy.typing.ArrayLike], names: Union[None, str, Tuple[str, ...], "ndpoly"] = None, dtype: Optional[numpy.typing.DTypeLike] = None, allocation: Optional[int] = None, retain_coefficients: Optional[bool] = None, retain_names: Optional[bool] = None, ) -> "ndpoly": """ Construct polynomial from polynomial attributes. Args: exponents: The exponents in an integer array with shape ``(N, D)``, where ``N`` is the number of terms in the polynomial sum and ``D`` is the number of dimensions. coefficients: The polynomial coefficients. Must correspond to `exponents` by having the same length ``N``. names: The indeterminants names, either as string names or as simple polynomials. Must correspond to the exponents by having length 1 or ``D``. If omitted, use ``numpoly.get_options()["default_varname"]``. dtype: The data type of the polynomial. If omitted, extract from `coefficients`. allocation: The maximum number of polynomial exponents. If omitted, use length of exponents for allocation. retain_coefficients: Do not remove redundant coefficients. If omitted use global defaults. retain_names: Do not remove redundant names. If omitted use global defaults. Returns: Polynomials with attributes defined by input. Examples: >>> numpoly.ndpoly.from_attributes( ... exponents=[[0]], coefficients=[4]) polynomial(4) >>> numpoly.ndpoly.from_attributes( ... exponents=[[1]], coefficients=[[1, 2, 3]]) polynomial([q0, 2*q0, 3*q0]) >>> numpoly.ndpoly.from_attributes( ... exponents=[[0], [1]], coefficients=[[0, 1], [1, 1]]) polynomial([q0, q0+1]) >>> numpoly.ndpoly.from_attributes( ... exponents=[[0, 1], [1, 1]], coefficients=[[0, 1], [1, 1]]) polynomial([q0*q1, q0*q1+q1]) """ return numpoly.polynomial_from_attributes( exponents=exponents, coefficients=coefficients, names=names, dtype=dtype, allocation=allocation, retain_coefficients=retain_coefficients, retain_names=retain_names, )
def set_dimensions(poly: PolyLike, dimensions: Optional[int] = None) -> ndpoly: """ Adjust the dimensions of a polynomial. Args: poly: Input polynomial dimensions: The dimensions of the output polynomial. If omitted, increase polynomial with one dimension. Returns: Polynomials with no internal dimensions. Unless the new dim is smaller then `poly`'s dimensions, the polynomial should have the same content. Examples: >>> q0, q1 = numpoly.variable(2) >>> poly = q0*q1-q0**2 >>> numpoly.set_dimensions(poly, 1) polynomial(-q0**2) >>> numpoly.set_dimensions(poly, 3) polynomial(q0*q1-q0**2) >>> numpoly.set_dimensions(poly).names ('q0', 'q1', 'q2') """ poly = numpoly.aspolynomial(poly) if dimensions is None: dimensions = len(poly.names)+1 diff = dimensions-len(poly.names) if diff > 0: padding = numpy.zeros((len(poly.exponents), diff), dtype="uint32") exponents = numpy.hstack([poly.exponents, padding]) coefficients = poly.coefficients varname = numpoly.get_options()["default_varname"] names_ = list(poly.names) idx = 0 while len(names_) < dimensions: if f"{varname}{idx}" not in names_: names_.append(f"{varname}{idx}") idx += 1 indices = numpy.lexsort([names_]) exponents = exponents[:, indices] names = tuple(names_[idx] for idx in indices) elif diff < 0: indices = True ^ numpy.any(poly.exponents[:, dimensions:], -1) exponents = poly.exponents[:, :dimensions] exponents = exponents[indices] coefficients = [ coeff for coeff, idx in zip(poly.coefficients, indices) if idx] names = poly.names[:dimensions] else: return poly return numpoly.polynomial_from_attributes( exponents=exponents, coefficients=coefficients, names=names, dtype=poly.dtype, allocation=poly.allocation, retain_names=True, )