Esempio n. 1
0
def setdim(poly, dim=None):
    """
    Adjust the dimensions of a polynomial.

    Output the results into ndpoly object

    Args:
        poly (chaospy.poly.ndpoly):
            Input polynomial
        dim (int):
            The dimensions of the output polynomial. If omitted, increase
            polynomial with one dimension. If the new dim is smaller then
            `poly`'s dimensions, variables with cut components are all cut.

    Examples:
        >>> q0, q1 = chaospy.variable(2)
        >>> poly = q0*q1-q0**2
        >>> chaospy.setdim(poly, 1)
        polynomial(-q0**2)
        >>> chaospy.setdim(poly, 3)
        polynomial(q0*q1-q0**2)
        >>> chaospy.setdim(poly).names
        ('q0', 'q1', 'q2')

    """
    poly = numpoly.polynomial(poly)
    indices = [int(name[1:]) for name in poly.names]
    dim = max(indices) + 2 if dim is None else dim
    poly = poly(**{("q%d" % index): 0 for index in indices if index >= dim})
    _, poly = numpoly.align_indeterminants(numpoly.symbols("q:%d" % dim), poly)
    return poly
Esempio n. 2
0
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,
    )
Esempio n. 3
0
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,
    )
Esempio n. 4
0
def multiply(
    x1: PolyLike,
    x2: PolyLike,
    out: Optional[ndpoly] = None,
    where: numpy.typing.ArrayLike = True,
    **kwargs: Any,
) -> ndpoly:
    """
    Multiply arguments element-wise.

    Args:
        x1, x2:
            Input arrays to be multiplied. 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:
        The product of `x1` and `x2`, element-wise. This is a scalar if
        both `x1` and `x2` are scalars.

    Examples:
        >>> poly = numpy.arange(9.0).reshape((3, 3))
        >>> q0q1q2 = numpoly.variable(3)
        >>> numpoly.multiply(poly, q0q1q2)
        polynomial([[0.0, q1, 2.0*q2],
                    [3.0*q0, 4.0*q1, 5.0*q2],
                    [6.0*q0, 7.0*q1, 8.0*q2]])

    """
    x1, x2 = numpoly.align_indeterminants(x1, x2)
    dtype = numpy.find_common_type([x1.dtype, x2.dtype], [])
    shape = numpy.broadcast_shapes(x1.shape, x2.shape)

    where = numpy.asarray(where)
    exponents = numpy.unique(numpy.tile(x1.exponents, (len(x2.exponents), 1)) +
                             numpy.repeat(x2.exponents, len(x1.exponents), 0),
                             axis=0)
    out_ = numpoly.ndpoly(
        exponents=exponents,
        shape=shape,
        names=x1.indeterminants,
        dtype=dtype,
    ) if out is None else out

    seen = set()
    for expon1, coeff1 in zip(x1.exponents, x1.coefficients):
        for expon2, coeff2 in zip(x2.exponents, x2.coefficients):
            key = (expon1 + expon2 + x1.KEY_OFFSET).ravel()
            key = key.view(f"U{len(expon1)}").item()
            if key in seen:
                out_.values[key] += numpy.multiply(coeff1,
                                                   coeff2,
                                                   where=where,
                                                   **kwargs)
            else:
                numpy.multiply(coeff1,
                               coeff2,
                               out=out_.values[key],
                               where=where,
                               **kwargs)
            seen.add(key)

    if out is None:
        out_ = numpoly.clean_attributes(out_)
    return out_
Esempio n. 5
0
def call(poly, *args, **kwargs):
    """
    Evaluate polynomial by inserting new values in to the indeterminants.

    Equaivalent to calling the polynomial or using the ``__call__`` method.

    Args:
        poly (numpoly.ndpoly):
            Polynomial to evaluate.
        args (int, float, numpy.ndarray, numpoly.ndpoly):
            Argument to evaluate indeterminants. Ordered positional by
            ``poly.indeterminants``.
        kwargs (int, float, numpy.ndarray, numpoly.ndpoly):
            Same as ``args``, but positioned by name.

    Returns:
        (numpoly.ndpoly):
            Evaluated polynomial.

    Examples:
        >>> x, y = numpoly.symbols("x y")
        >>> poly = numpoly.polynomial([[x, x-1], [y, y+x]])
        >>> poly()
        polynomial([[x, -1+x],
                    [y, x+y]])
        >>> poly
        polynomial([[x, -1+x],
                    [y, x+y]])
        >>> poly(1, 0)
        array([[1, 0],
               [0, 1]])
        >>> poly(1, y=[0, 1, 2])
        array([[[1, 1, 1],
                [0, 0, 0]],
        <BLANKLINE>
               [[0, 1, 2],
                [1, 2, 3]]])
        >>> poly(y)
        polynomial([[y, -1+y],
                    [y, 2*y]])
        >>> poly(y=x-1, x=2*y)
        polynomial([[2*y, -1+2*y],
                    [-1+x, -1+2*y+x]])

    """
    # Make sure kwargs contains all args and nothing but indeterminants:
    for arg, indeterminant in zip(args, poly.names):
        if indeterminant in kwargs:
            raise TypeError(
                "multiple values for argument '%s'" % indeterminant)
        kwargs[indeterminant] = arg
    extra_args = [key for key in kwargs if key not in poly.names]
    if extra_args:
        raise TypeError("unexpected keyword argument '%s'" % extra_args[0])

    if not kwargs:
        return poly.copy()

    # Saturate kwargs with values not given:
    indeterminants = poly.indeterminants
    for indeterminant in indeterminants:
        name = indeterminant.names[0]
        if name not in kwargs:
            kwargs[name] = indeterminant

    # There can only be one shape:
    ones = numpy.ones((), dtype=int)
    for value in kwargs.values():
        ones = ones * numpy.ones(numpoly.polynomial(value).shape, dtype=int)

    # main loop:
    out = 0
    for exponent, coefficient in zip(poly.exponents, poly.coefficients):
        term = ones
        for power, name in zip(exponent, poly.names):
            term = term*kwargs[name]**power
        shape = coefficient.shape+ones.shape
        out = out+numpoly.outer(coefficient, term).reshape(shape)

    if out.isconstant():
        return out.tonumpy()
    out, _ = numpoly.align_indeterminants(out, indeterminants)
    return out
Esempio n. 6
0
def call(
        poly: PolyLike,
        args: Sequence[Optional[PolyLike]] = (),
        kwargs: Dict[str, PolyLike] = None,
) -> Union[numpy.ndarray, ndpoly]:
    """
    Evaluate polynomial by inserting new values in to the indeterminants.

    Equivalent to calling the polynomial or using the ``__call__`` method.

    Args:
        poly:
            Polynomial to evaluate.
        args:
            Argument to evaluate indeterminants. Ordered positional by
            ``poly.indeterminants``. None values indicate that a variable is
            not to be evaluated, creating a partial evaluation.
        kwargs:
            Same as ``args``, but positioned by name.

    Returns:
        Evaluated polynomial. If the resulting array does not contain any
        indeterminants, an array is returned instead of a polynomial.

    Examples:
        >>> q0, q1 = numpoly.variable(2)
        >>> poly = numpoly.polynomial([[q0, q0-1], [q1, q1+q0]])
        >>> numpoly.call(poly)
        polynomial([[q0, q0-1],
                    [q1, q1+q0]])
        >>> poly
        polynomial([[q0, q0-1],
                    [q1, q1+q0]])
        >>> numpoly.call(poly, (1, 0))
        array([[1, 0],
               [0, 1]])
        >>> numpoly.call(poly, (1,), {"q1": [0, 1, 2]})
        array([[[1, 1, 1],
                [0, 0, 0]],
        <BLANKLINE>
               [[0, 1, 2],
                [1, 2, 3]]])
        >>> numpoly.call(poly, (q1,))
        polynomial([[q1, q1-1],
                    [q1, 2*q1]])
        >>> numpoly.call(poly, kwargs={"q1": q0-1, "q0": 2*q1})
        polynomial([[2*q1, 2*q1-1],
                    [q0-1, 2*q1+q0-1]])

    """
    logger = logging.getLogger(__name__)

    poly = numpoly.aspolynomial(poly)
    kwargs = kwargs if kwargs else {}

    parameters = dict(zip(poly.names, poly.indeterminants))
    if kwargs:
        parameters.update(kwargs)
    for arg, name in zip(args, poly.names):
        if name in kwargs:
            raise TypeError(f"multiple values for argument '{name}'")
        if arg is not None:
            parameters[name] = arg
    extra_args = [key for key in parameters if key not in poly.names]
    if extra_args:
        raise TypeError(f"unexpected keyword argument '{extra_args[0]}'")

    # There can only be one shape:
    ones = numpy.ones((), dtype=int)
    for value in parameters.values():
        ones = ones*numpy.ones(numpoly.polynomial(value).shape, dtype=int)
    shape = poly.shape+ones.shape

    logger.debug("poly shape: %s", poly.shape)
    logger.debug("parameter common shape: %s", ones.shape)
    logger.debug("output shape: %s", shape)

    # main loop:
    out = numpy.zeros((), dtype=int)
    for exponent, coefficient in zip(poly.exponents, poly.coefficients):
        term = ones
        for power, name in zip(exponent, poly.names):
            term = term*parameters[name]**power
        if isinstance(term, numpoly.ndpoly):
            tmp = numpoly.outer(coefficient, term)
        else:
            tmp = numpy.outer(coefficient, term)
        out = out+tmp.reshape(shape)

    if isinstance(out, numpoly.ndpoly):
        if out.isconstant():
            out = out.tonumpy()
        else:
            out, _ = numpoly.align_indeterminants(out, poly.indeterminants)
    return out
Esempio n. 7
0
def copyto(
    dst: ndpoly,
    src: PolyLike,
    casting: str = "same_kind",
    where: numpy.typing.ArrayLike = True,
) -> None:
    """
    Copy values from one array to another, broadcasting as necessary.

    Raises a TypeError if the `casting` rule is violated, and if
    `where` is provided, it selects which elements to copy.

    Args:
        dst:
            The array into which values are copied.
        src:
            The array from which values are copied.
        casting:
            Controls what kind of data casting may occur when copying.

            * 'no' means the data types should not be cast at all.
            * 'equiv' means only byte-order changes are allowed.
            * 'safe' means only casts which can preserve values are allowed.
            * 'same_kind' means only safe casts or casts within a kind,
                like float64 to float32, are allowed.
            * 'unsafe' means any data conversions may be done.
        where:
            A boolean array which is broadcasted to match the dimensions
            of `dst`, and selects elements to copy from `src` to `dst`
            wherever it contains the value True.

    Examples:
        >>> q0 = numpoly.variable()
        >>> poly1 = numpoly.polynomial([1, q0**2, q0])
        >>> poly2 = numpoly.polynomial([2, q0, 3])
        >>> numpoly.copyto(poly1, poly2, where=[True, False, True])
        >>> poly1
        polynomial([2, q0**2, 3])
        >>> numpoly.copyto(poly1, poly2)
        >>> poly1
        polynomial([2, q0, 3])

    """
    logger = logging.getLogger(__name__)
    src = numpoly.aspolynomial(src)
    assert isinstance(dst, numpy.ndarray)
    if not isinstance(dst, numpoly.ndpoly):
        if dst.dtype.names is None:
            if src.isconstant():
                return numpy.copyto(dst,
                                    src.tonumpy(),
                                    casting=casting,
                                    where=where)
            raise ValueError(f"Could not convert src {src} to dst {dst}")
        if casting != "unsafe":
            raise ValueError(
                f"could not safely convert src {src} to dst {dst}")
        logger.warning("Copying ndpoly input into ndarray")
        logger.warning("You might need to cast `numpoly.polynomial(dst)`.")
        logger.warning("Indeterminant names might be lost.")
        dst_keys = dst.dtype.names
        dst_ = dst
    else:
        dst_keys = dst.keys
        src, _ = numpoly.align_indeterminants(src, dst)
        dst_ = dst.values

    missing_keys = set(src.keys).difference(dst_keys)
    if missing_keys:
        raise ValueError(f"memory layouts are incompatible: {missing_keys}")

    for key in dst_keys:
        if key in src.keys:
            numpy.copyto(dst_[key],
                         src.values[key],
                         casting=casting,
                         where=where)
        else:
            numpy.copyto(dst_[key],
                         numpy.array(False, dtype=dst_[key].dtype),
                         casting=casting,
                         where=where)