示例#1
0
def test_numpoly_ndpoly():
    poly = numpoly.ndpoly(exponents=[(1, )], shape=(), names="X")
    poly["<"] = 1
    assert poly == X
    poly = numpoly.ndpoly(exponents=[(1, )], shape=(), names=X)
    poly["<"] = 1
    assert poly == X
    poly = numpoly.ndpoly(exponents=[(1, 0), (0, 1)],
                          shape=(),
                          names=("X", "Y"))
    poly["<;"] = 2
    poly[";<"] = 3
    assert poly == 2 * X + 3 * Y
    poly = numpoly.ndpoly(exponents=[(1, 0), (0, 1)], shape=(2, ), names="Q")
    poly["<;"] = [1, 0]
    poly[";<"] = [0, 1]
    assert numpy.all(poly == numpoly.symbols("Q0 Q1"))
示例#2
0
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
示例#3
0
def test_ndpoly():
    poly = numpoly.ndpoly(exponents=[(1, )], shape=(), names="q0")
    poly["<"] = 1
    assert poly == X
    poly = numpoly.ndpoly(exponents=[(1, )], shape=(), names=X)
    poly["<"] = 1
    assert poly == X
    poly = numpoly.ndpoly(exponents=[(1, 0), (0, 1)],
                          shape=(),
                          names=("q0", "q1"))
    poly["<;"] = 2
    poly[";<"] = 3
    assert poly == 2 * X + 3 * Y
    with numpoly.global_options(varname_filter=r"Q\d+"):
        poly = numpoly.ndpoly(exponents=[(1, 0), (0, 1)],
                              shape=(2, ),
                              names="Q:2")
        poly["<;"] = [1, 0]
        poly[";<"] = [0, 1]
        assert numpy.all(poly == numpoly.symbols("Q0 Q1"))
示例#4
0
def simple_dispatch(
        numpy_func,
        inputs,
        out=None,
        **kwargs
):
    """
    Dispatch function between numpy and numpoly.

    Assumes that evaluation can be performed on the coefficients alone and that
    there are no change to the polynomials.

    Args:
        numpy_func (Callable):
            The numpy function to evaluate `inputs` on.
        inputs (Iterable[numpoly.ndpoly]):
            One or more input arrays.
        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.
        kwargs:
            Keyword args passed to `numpy_func`.

    Returns:
        (numpoly.ndpoly):
            Polynomial, where the coefficients from `input` are passed to
            `numpy_func` to create the output coefficients.

    """
    inputs = numpoly.align_polynomials(*inputs)
    no_output = out is None
    for key in inputs[0].keys:

        if out is None:
            tmp = numpy_func(*[poly[key] for poly in inputs], **kwargs)
            out = numpoly.ndpoly(
                exponents=inputs[0].exponents,
                shape=tmp.shape,
                names=inputs[0].indeterminants,
                dtype=tmp.dtype,
            )
            out[key] = tmp
        else:
            tmp = numpy_func(
                *[poly[key] for poly in inputs], out=out[key], **kwargs)

    if no_output:
        out = numpoly.clean_attributes(out)
    return out
示例#5
0
def simple_dispatch(numpy_func: Callable,
                    inputs: Sequence[Any],
                    out: Optional[Tuple[ndpoly, ...]] = None,
                    **kwargs: Any) -> ndpoly:
    """
    Dispatch function between numpy and numpoly.

    Assumes that evaluation can be performed on the coefficients alone and that
    there are no change to the polynomials.

    Args:
        numpy_func:
            The numpy function to evaluate `inputs` on.
        inputs:
            One or more input arrays.
        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.
        kwargs:
            Keyword args passed to `numpy_func`.

    Returns:
        Polynomial, where the coefficients from `input` are passed to
        `numpy_func` to create the output coefficients.

    """
    inputs = numpoly.align_polynomials(*inputs)
    keys = (inputs[0] if out is None else numpoly.aspolynomial(out[0])).keys

    tmp = numpy_func(*[poly.values[keys[0]] for poly in inputs], **kwargs)
    if out is None:
        out_ = numpoly.ndpoly(
            exponents=inputs[0].exponents,
            shape=tmp.shape,
            names=inputs[0].indeterminants,
            dtype=tmp.dtype,
        )
    else:
        assert len(out) == 1
        out_ = out[0]
    out_.values[keys[0]] = tmp

    for key in keys[1:]:
        out_.values[key] = numpy_func(*[poly.values[key] for poly in inputs],
                                      **kwargs)

    if out is None:
        out_ = numpoly.clean_attributes(out_)
    return numpoly.aspolynomial(out_)
示例#6
0
def test_floor_divide(interface):
    """Tests for numpoly.floor_divide."""
    poly = polynomial([[0., 2. * Y], [X, 2.]])
    assert_equal(interface.floor_divide(poly, 2), [[0., Y], [0., 1]])
    assert_equal(interface.floor_divide(poly, [1., 2.]), [[0., Y], [X, 1]])
    assert_equal(interface.floor_divide(poly, [[1., 2.], [2., 1.]]),
                 [[0., Y], [0., 2.]])
    with raises(numpoly.FeatureNotSupported):
        interface.floor_divide(poly, X)
    with raises(numpoly.FeatureNotSupported):
        poly.__floordiv__(poly)
    with raises(numpoly.FeatureNotSupported):
        poly.__rfloordiv__(poly)

    out = numpoly.ndpoly(
        exponents=poly.exponents,
        shape=(2, 2),
        names=("q0", "q1"),
        dtype=float,
    )
    numpoly.floor_divide(poly, 2, out=out)
    assert_equal(out, [[0., Y], [0., 1.]])
示例#7
0
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)
示例#8
0
def divide(x1, x2, out=None, where=True, **kwargs):
    """
    Return a true division of the inputs, element-wise.

    Instead of the Python traditional 'floor division', this returns a true
    division.  True division adjusts the output type to present the best
    answer, regardless of input types.

    Args:
        x1 (numpoly.ndpoly):
            Dividend array.
        x2 (numpoly.ndpoly):
            Divisor array. If ``x1.shape != x2.shape``, they must be
            broadcastable to a commo n 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):
            This is a scalar if both `x1` and `x2` are scalars.

    Examples:
        >>> xyz = numpoly.symbols("x y z")
        >>> numpoly.divide(xyz, 4)
        polynomial([0.25*x, 0.25*y, 0.25*z])
        >>> numpoly.divide(xyz, [1, 2, 4])
        polynomial([x, 0.5*y, 0.25*z])
        >>> numpoly.divide([1, 2, 4], xyz)
        Traceback (most recent call last):
            ...
        ValueError: only constant polynomials can be converted to array.

    """
    x1, x2 = numpoly.align_polynomials(x1, x2)
    x2 = x2.tonumpy()
    no_output = out is None
    if no_output:
        out = numpoly.ndpoly(
            exponents=x1.exponents,
            shape=x1.shape,
            names=x1.indeterminants,
            dtype=numpy.common_type(x1, numpy.array(1.)),
        )
    elif not isinstance(out, numpy.ndarray):
        assert len(out) == 1, "only one output"
        out = out[0]
    for key in x1.keys:
        out[key] = 0
        numpy.true_divide(x1[key], x2, out=out[key], where=where, **kwargs)
    if no_output:
        out = numpoly.clean_attributes(out)
    return out
示例#9
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,
    )
示例#10
0
def polynomial(
    poly_like=None,
    names=None,
    dtype=None,
):
    """
    Attempt to cast an object into a polynomial array.

    Supports various casting options:

    ==================  =======================================================
    ``dict``            Keys are tuples that represent polynomial exponents,
                        and values are numpy arrays that represents polynomial
                        coefficients.
    ``numpoly.ndpoly``  Copy of the polynomial.
    ``numpy.ndarray``   Constant term polynomial.
    ``sympy.Poly``      Convert polynomial from ``sympy`` to ``numpoly``,
                        if possible.
    ``Iterable``        Multivariate array construction.
    structured array    Assumes that the input are raw polynomial core and can
                        be used to construct a polynomial without changing the
                        data. Used for developer convenience.
    ==================  =======================================================

    Args:
        poly_like (typing.Any):
            Input to be converted to a `numpoly.ndpoly` polynomial type.
        names (str, typing.Tuple[str, ...]):
            Name of the indeterminant variables. If possible to infer from
            ``poly_like``, this argument will be ignored.
        dtype (type, numpy.dtype):
            Data type used for the polynomial coefficients.

    Returns:
        (numpoly.ndpoly):
            Polynomial based on input ``poly_like``.

    Examples:
        >>> numpoly.polynomial({(1,): 1})
        polynomial(q)
        >>> x, y = numpoly.symbols("x y")
        >>> x**2 + x*y + 2
        polynomial(x**2+x*y+2)
        >>> -3*x + x**2 + y
        polynomial(-3*x+x**2+y)
        >>> numpoly.polynomial([x*y, x, y])
        polynomial([x*y, x, y])
        >>> numpoly.polynomial([1, 2, 3])
        polynomial([1, 2, 3])
        >>> import sympy
        >>> x_, y_ = sympy.symbols("x, y")
        >>> numpoly.polynomial(3*x_*y_ - 4 + x_**5)
        polynomial(x**5+3*x*y-4)

    """
    if poly_like is None:
        poly = numpoly.ndpoly(
            exponents=[(0, )],
            shape=(),
            names=names,
            dtype=dtype,
        )
        poly[";"] = 0

    elif isinstance(poly_like, dict):
        poly = numpoly.ndpoly(exponents=[(0, )], shape=())
        exponents, coefficients = zip(*list(poly_like.items()))
        poly = numpoly.ndpoly.from_attributes(
            exponents=exponents,
            coefficients=coefficients,
            names=names,
            dtype=dtype,
        )

    elif isinstance(poly_like, numpoly.ndpoly):
        if names is None:
            names = poly_like.names
        poly = numpoly.ndpoly.from_attributes(
            exponents=poly_like.exponents,
            coefficients=poly_like.coefficients,
            names=names,
            dtype=dtype,
        )

    # assume polynomial converted to structured array
    elif isinstance(poly_like, numpy.ndarray) and poly_like.dtype.names:

        keys = numpy.asarray(poly_like.dtype.names, dtype="U")
        exponents = keys.flatten().view(
            numpy.uint32) - numpoly.ndpoly.KEY_OFFSET
        exponents = exponents.reshape(len(keys), -1)

        coefficients = [poly_like[key] for key in poly_like.dtype.names]
        poly = numpoly.ndpoly.from_attributes(
            exponents=exponents,
            coefficients=coefficients,
            names=names,
        )

    elif isinstance(poly_like, (int, float, numpy.ndarray, numpy.generic)):
        poly = numpoly.ndpoly.from_attributes(
            exponents=[(0, )],
            coefficients=numpy.array([poly_like]),
            names=names,
            dtype=dtype,
        )

    # handler for sympy objects
    elif hasattr(poly_like, "as_poly"):
        poly_like = poly_like.as_poly()
        exponents = poly_like.monoms()
        coefficients = [
            int(coeff) if coeff.is_integer else float(coeff)
            for coeff in poly_like.coeffs()
        ]
        names = [str(elem) for elem in poly_like.gens]
        poly = numpoly.ndpoly.from_attributes(
            exponents=exponents,
            coefficients=coefficients,
            names=names,
        )

    else:
        poly = compose_polynomial_array(
            arrays=poly_like,
            dtype=dtype,
        )

    return poly
示例#11
0
def floor_divide(x1, x2, out=None, where=True, **kwargs):
    """
    Return the largest integer smaller or equal to the division of the inputs.

    It is equivalent to the Python ``//`` operator and pairs with the
    Python ``%`` (`remainder`), function so that ``a = a % b + b * (a // b)``
    up to roundoff.

    Args:
        x1 (numpoly.ndpoly):
            Numerator.
        x2 (numpoly.ndpoly):
            Denominator. 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):
            This is a scalar if both `x1` and `x2` are scalars.

    Examples:
        >>> xyz = [1, 2, 4]*numpoly.symbols("x y z")
        >>> numpoly.floor_divide(xyz, 2.)
        polynomial([0.0, y, 2.0*z])
        >>> numpoly.floor_divide(xyz, [1, 2, 4])
        polynomial([x, y, z])
        >>> numpoly.floor_divide([1, 2, 4], xyz)
        Traceback (most recent call last):
            ...
        ValueError: only constant polynomials can be converted to array.

    """
    x1, x2 = numpoly.align_polynomials(x1, x2)
    x2 = x2.tonumpy()
    no_output = out is None
    if no_output:
        out = numpoly.ndpoly(
            exponents=x1.exponents,
            shape=x1.shape,
            names=x1.indeterminants,
            dtype=numpy.common_type(x1, numpy.array(1.)),
        )
    for key in x1.keys:
        numpy.floor_divide(x1[key], x2, out=out[key], where=where, **kwargs)
    if no_output:
        out = numpoly.clean_attributes(out)
    return out
示例#12
0
def polynomial_from_attributes(
    exponents,
    coefficients,
    names,
    dtype=None,
    clean=True,
):
    """
    Construct polynomial from polynomial attributes.

    Args:
        exponents (numpy.ndarray):
            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 (Iterable[numpy.ndarray]):
            The polynomial coefficients. Must correspond to `exponents` by
            having the same length ``N``.
        names (Union[Sequence[str], numpoly.ndpoly]):
            The indeterminant names, either as string names or as
            simple polynomials. Must correspond to the exponents by having
            length ``D``.
        dtype (Optional[numpy.dtype]):
            The data type of the polynomial. If omitted, extract from
            `coefficients`.
        clean (bool):
            Clean up attributes, removing redundant indeterminant names and
            exponents.

    Returns:
        (numpoly.ndpoly):
            Polynomial array with attributes determined by the input.

    Examples:
        >>> numpoly.ndpoly.from_attributes(
        ...     exponents=[(0,), (1,)],
        ...     coefficients=[[1, 0], [0, 1]],
        ...     names=("x",),
        ... )
        polynomial([1, x])
        >>> numpoly.ndpoly.from_attributes(
        ...     exponents=[(0, 0, 0), (1, 1, 2)],
        ...     coefficients=[4, -1],
        ...     names=("x", "y", "z"),
        ... )
        polynomial(4-x*y*z**2)
        >>> numpoly.ndpoly.from_attributes(
        ...     exponents=[(0,)],
        ...     coefficients=[0],
        ... )
        polynomial(0)

    """
    if clean:
        exponents, coefficients, names = clean_.postprocess_attributes(
            exponents, coefficients, names)
    if coefficients:
        dtype = coefficients[0].dtype if dtype is None else dtype
        shape = coefficients[0].shape
    else:
        dtype = dtype if dtype else int
        shape = ()
    poly = numpoly.ndpoly(
        exponents=exponents,
        shape=shape,
        names=names,
        dtype=dtype,
    )
    for exponent, values in zip(poly.keys, coefficients):
        poly[exponent] = values
    return poly
示例#13
0
def polynomial_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 indeterminant names, either as string names or as
            simple polynomials. Must correspond to the exponents by having
            length ``D``.
        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:
        Polynomial array with attributes determined by the input.

    Examples:
        >>> numpoly.ndpoly.from_attributes(
        ...     exponents=[(0,), (1,)],
        ...     coefficients=[[1, 0], [0, 1]],
        ...     names="q4",
        ... )
        polynomial([1, q4])
        >>> numpoly.ndpoly.from_attributes(
        ...     exponents=[(0, 0, 0), (1, 1, 2)],
        ...     coefficients=[4, -1],
        ...     names=("q2", "q4", "q10"),
        ... )
        polynomial(-q2*q4*q10**2+4)
        >>> numpoly.ndpoly.from_attributes(
        ...     exponents=[(0,)],
        ...     coefficients=[0],
        ... )
        polynomial(0)

    """
    exponents, coefficients, names = clean.postprocess_attributes(
        exponents=exponents,
        coefficients=coefficients,
        names=names,
        retain_coefficients=retain_coefficients,
        retain_names=retain_names,
    )
    if coefficients:
        dtype = coefficients[0].dtype if dtype is None else dtype
        shape = coefficients[0].shape
    else:
        dtype = dtype if dtype else int
        shape = ()
    poly = numpoly.ndpoly(
        exponents=exponents,
        shape=shape,
        names=names,
        dtype=dtype,
        allocation=allocation,
    )
    for key, values in zip(poly.keys, coefficients):
        poly.values[key] = values
    return poly
示例#14
0
def floor_divide(
    x1: PolyLike,
    x2: PolyLike,
    out: Optional[ndpoly] = None,
    where: Optional[numpy.ndarray] = numpy.array(True),
    **kwargs: Any,
) -> ndpoly:
    """
    Return the largest integer smaller or equal to the division of the inputs.

    It is equivalent to the Python ``//`` operator and pairs with the
    Python ``%`` (`remainder`), function so that ``a = a % b + b * (a // b)``
    up to roundoff.

    Args:
        x1:
            Dividend.
        x2:
            Divisor. 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:
        This is a scalar if both `x1` and `x2` are scalars.

    Raises:
        ValueError:
            If `x2` contains indeterminants, numerical division is no longer
            possible and an error is raised instead. For polynomial
            division see ``numpoly.poly_divide``.

    Examples:
        >>> numpoly.floor_divide([1, 3, 5], 2)
        polynomial([0, 1, 2])
        >>> poly = [1, 2, 4]*numpoly.variable(3)
        >>> poly
        polynomial([q0, 2*q1, 4*q2])
        >>> numpoly.floor_divide(poly, 2.)
        polynomial([0.0, q1, 2.0*q2])
        >>> numpoly.floor_divide(poly, [1, 2, 4])
        polynomial([q0, q1, q2])

    """
    x1, x2 = numpoly.align_polynomials(x1, x2)
    if not x2.isconstant():
        raise numpoly.FeatureNotSupported(DIVIDE_ERROR_MSG)
    x2 = x2.tonumpy()
    dtype = numpy.common_type(x1, x2)
    if x1.dtype == x2.dtype == "int64":
        dtype = "int64"
    no_output = out is None
    if out is None:
        out = numpoly.ndpoly(
            exponents=x1.exponents,
            shape=x1.shape,
            names=x1.indeterminants,
            dtype=dtype,
        )
    assert isinstance(out, numpoly.ndpoly)
    for key in x1.keys:
        out.values[key] = 0
        numpy.floor_divide(x1.values[key],
                           x2,
                           out=out.values[key],
                           where=where,
                           **kwargs)
    if no_output:
        out = numpoly.clean_attributes(out)
    return out
示例#15
0
def polynomial(
    poly_like: PolyLike = 0,
    names: Union[None, str, Tuple[str, ...], ndpoly] = None,
    dtype: Optional[numpy.typing.DTypeLike] = None,
    allocation: Optional[int] = None,
) -> ndpoly:
    """
    Attempt to cast an object into a polynomial array.

    Supports various casting options:

    ==================  =======================================================
    ``dict``            Keys are tuples that represent polynomial exponents,
                        and values are numpy arrays that represents polynomial
                        coefficients.
    ``numpoly.ndpoly``  Copy of the polynomial.
    ``numpy.ndarray``   Constant term polynomial.
    ``sympy.Poly``      Convert polynomial from ``sympy`` to ``numpoly``,
                        if possible.
    ``Iterable``        Multivariate array construction.
    structured array    Assumes that the input are raw polynomial core and can
                        be used to construct a polynomial without changing the
                        data. Used for developer convenience.
    ==================  =======================================================

    Args:
        poly_like:
            Input to be converted to a `numpoly.ndpoly` polynomial type.
        names:
            Name of the indeterminant variables. If possible to infer from
            ``poly_like``, this argument will be ignored.
        dtype:
            Data type used for the polynomial coefficients.
        allocation:
            The maximum number of polynomial exponents. If omitted, use
            length of exponents for allocation.

    Returns:
        Polynomial based on input ``poly_like``.

    Examples:
        >>> numpoly.polynomial({(1,): 1})
        polynomial(q0)
        >>> q0, q1 = numpoly.variable(2)
        >>> q0**2+q0*q1+2
        polynomial(q0*q1+q0**2+2)
        >>> -3*q0+q0**2+q1
        polynomial(q0**2+q1-3*q0)
        >>> numpoly.polynomial([q0*q1, q0, q1])
        polynomial([q0*q1, q0, q1])
        >>> numpoly.polynomial([1, 2, 3])
        polynomial([1, 2, 3])
        >>> import sympy
        >>> q0_, q1_ = sympy.symbols("q0, q1")
        >>> numpoly.polynomial(3*q0_*q1_-4+q0_**5)
        polynomial(q0**5+3*q0*q1-4)

    """
    if isinstance(poly_like, dict):
        poly = numpoly.ndpoly(exponents=[(0, )], shape=())
        exponents, coefficients = zip(*list(poly_like.items()))
        poly = numpoly.ndpoly.from_attributes(
            exponents=exponents,
            coefficients=coefficients,
            names=names,
            dtype=dtype,
            allocation=allocation,
        )

    elif isinstance(poly_like, numpoly.ndpoly):
        if names is None:
            names = poly_like.names
        poly = numpoly.ndpoly.from_attributes(
            exponents=poly_like.exponents,
            coefficients=poly_like.coefficients,
            names=names,
            dtype=dtype,
            allocation=allocation,
        )

    # assume polynomial converted to structured array
    elif isinstance(poly_like, numpy.ndarray) and poly_like.dtype.names:

        keys = numpy.asarray(poly_like.dtype.names, dtype="U")
        keys = numpy.array([key for key in keys if not key.isdigit()])
        keys = numpy.array(keys,
                           dtype=f"U{numpy.max(numpy.char.str_len(keys))}")
        exponents = keys.view(numpy.uint32) - numpoly.ndpoly.KEY_OFFSET
        exponents = exponents.reshape(len(keys), -1)

        coefficients = [poly_like[key] for key in poly_like.dtype.names]
        poly = numpoly.ndpoly.from_attributes(
            exponents=exponents,
            coefficients=coefficients,
            names=names,
            allocation=allocation,
        )

    elif isinstance(poly_like, (int, float, numpy.ndarray, numpy.generic)):
        poly = numpoly.ndpoly.from_attributes(
            exponents=[(0, )],
            coefficients=[numpy.asarray(poly_like)],
            names=names,
            dtype=dtype,
            allocation=allocation,
        )

    # handler for sympy objects
    elif hasattr(poly_like, "as_poly"):
        poly_like = poly_like.as_poly()  # type: ignore
        exponents = poly_like.monoms()  # type: ignore
        coefficients = [
            int(coeff) if coeff.is_integer else float(coeff)  # type: ignore
            for coeff in poly_like.coeffs()  # type: ignore
        ]
        names = [str(elem) for elem in poly_like.gens]  # type: ignore
        poly = numpoly.ndpoly.from_attributes(
            exponents=exponents,
            coefficients=coefficients,
            names=names,
            allocation=allocation,
        )

    else:
        poly = compose_polynomial_array(
            arrays=poly_like,  # type: ignore
            dtype=dtype,
            allocation=allocation,
        )

    return poly
示例#16
0
def monomial(start, stop=None, ordering="G", cross_truncation=1., names=None):
    """
    Create an polynomial monomial expansion.

    Args:
        start (int, numpy.ndarray):
            The minimum polynomial to include. If int is provided, set as
            lowest total order. If array of int, set as lower order along each
            indeterminant.
        stop (int, numpy.ndarray):
            The maximum shape included. If omitted:
            ``stop <- start; start <- 0`` If int is provided, set as largest
            total order. If array of int, set as largest order along each
            indeterminant.
        ordering (str):
            The monomial ordering where the letters ``G``, ``I`` and ``R`` can
            be used to set grade, inverse and reverse to the ordering. For
            ``names=("x", "y"))`` we get for various values for
            ``ordering``:

            ========  =====================
            ordering  output
            ========  =====================
            ""        [1 y y**2 x x*y x**2]
            "G"       [1 y x y**2 x*y x**2]
            "I"       [x**2 x*y x y**2 y 1]
            "R"       [1 x x**2 y x*y y**2]
            "GIR"     [y**2 x*y x**2 y x 1]
            ========  =====================
        cross_truncation (float):
            Use hyperbolic cross truncation scheme to reduce the number of
            terms in expansion.
        names (None, numpoly.ndpoly, str, Tuple[str, ...])
            The indeterminants names used to create the monomials expansion.

    Returns:
        (numpoly.ndpoly):
            Monomial expansion.

    Examples:
        >>> numpoly.monomial(4)
        polynomial([1, q, q**2, q**3])
        >>> numpoly.monomial(4, 5, ordering="GR", names=("x", "y"))
        polynomial([x**4, x**3*y, x**2*y**2, x*y**3, y**4])
        >>> numpoly.monomial(2, [3, 4])
        polynomial([q1**2, q0*q1, q0**2, q1**3])
        >>> numpoly.monomial(0, 5, names=("x", "y"), cross_truncation=0.5)
        polynomial([1, y, x, y**2, x*y, x**2, y**3, x**3, y**4, x**4])

    """
    if stop is None:
        start, stop = numpy.array(0), start

    start = numpy.array(start, dtype=int)
    stop = numpy.array(stop, dtype=int)
    dimensions = 1 if names is None else len(names)
    dimensions = max(start.size, stop.size, dimensions)

    indices = bindex(
        start=start,
        stop=stop,
        dimensions=dimensions,
        ordering=ordering,
        cross_truncation=cross_truncation,
    )

    poly = numpoly.ndpoly(
        exponents=indices,
        shape=(len(indices), ),
        names=names,
    )
    for coeff, key in zip(numpy.eye(len(indices), dtype=int), poly.keys):
        poly[key] = coeff
    return poly
示例#17
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_
示例#18
0
def monomial(
    start: numpy.typing.ArrayLike,
    stop: Optional[numpy.typing.ArrayLike] = None,
    dimensions: int = 1,
    cross_truncation: numpy.typing.ArrayLike = 1.,
    graded: bool = False,
    reverse: bool = False,
    allocation: Optional[int] = None,
) -> ndpoly:
    """
    Create an polynomial monomial expansion.

    Args:
        start:
            The minimum polynomial to include. If int is provided, set as
            lowest total order. If array of int, set as lower order along each
            indeterminant.
        stop:
            The maximum shape included. If omitted:
            ``stop <- start; start <- 0`` If int is provided, set as largest
            total order. If array of int, set as largest order along each
            indeterminant.
        dimensions:
            The indeterminants names used to create the monomials expansion.
            If int provided, set the number of names to be used.
        cross_truncation:
            The hyperbolic cross truncation scheme to reduce the number of
            terms in expansion. In other words, it sets the :math:`q` in the
            :math:`L_q`-norm.
        graded:
            Graded sorting, meaning the indices are always sorted by the index
            sum. E.g. ``q0**2*q1**2*q2**2`` has an exponent sum of 6, and will
            therefore be consider larger than both ``q0**3*q1*q2``,
            ``q0*q1**2*q2`` and ``q0*q1*q2**2``, which all have exponent
            sum of 5.
        reverse:
            Reverse lexicographical sorting meaning that ``q0*q1**3`` is
            considered bigger than ``q0**3*q1``, instead of the opposite.
        allocation:
            The maximum number of polynomial exponents. If omitted, use
            length of exponents for allocation.

    Returns:
        Monomial expansion.

    Examples:
        >>> numpoly.monomial(4)
        polynomial([1, q0, q0**2, q0**3])
        >>> numpoly.monomial(start=4, stop=5, dimensions=2,
        ...                  graded=True, reverse=True)
        polynomial([q1**4, q0*q1**3, q0**2*q1**2, q0**3*q1, q0**4])
        >>> numpoly.monomial(2, [3, 4], graded=True)
        polynomial([q0**2, q0*q1, q1**2, q1**3])
        >>> numpoly.monomial(
        ...     start=0,
        ...     stop=4,
        ...     dimensions=("q2", "q4"),
        ...     cross_truncation=0.5,
        ...     graded=True,
        ...     reverse=True,
        ... )
        polynomial([1, q4, q2, q4**2, q2**2, q4**3, q2**3])

    """
    if stop is None:
        start, stop = numpy.array(0), start

    start = numpy.array(start, dtype=int)
    stop = numpy.array(stop, dtype=int)
    if isinstance(dimensions, str):
        names, dimensions = (dimensions, ), 1
    elif isinstance(dimensions, int):
        dimensions = max(start.size, stop.size, dimensions)
        names = numpoly.variable(dimensions).names
    elif dimensions is None:
        dimensions = max(start.size, stop.size)
        names = numpoly.variable(dimensions).names
    else:
        names, dimensions = dimensions, len(dimensions)

    indices = numpoly.glexindex(
        start=start,
        stop=stop,
        dimensions=dimensions,
        graded=graded,
        reverse=reverse,
        cross_truncation=cross_truncation,
    )
    poly = numpoly.ndpoly(
        exponents=indices,
        shape=(len(indices), ),
        names=names,
        allocation=allocation,
    )
    for coeff, key in zip(numpy.eye(len(indices), dtype=int), poly.keys):
        numpy.ndarray.__setitem__(poly, key, coeff)
    return poly
示例#19
0
def apply_along_axis(
    func1d: Callable[[PolyLike], PolyLike],
    axis: int,
    arr: PolyLike,
    *args: Any,
    **kwargs: Any,
) -> ndpoly:
    """
    Apply a function to 1-D slices along the given axis.

    Execute `func1d(a, *args)` where `func1d` operates on 1-D arrays and `a` is
    a 1-D slice of `arr` along `axis`.

    This is equivalent to (but faster than) the following use of `ndindex` and
    `s_`, which sets each of ``ii``, ``jj``, and ``kk`` to a tuple of indices::

        Ni, Nk = a.shape[:axis], a.shape[axis+1:]
        for ii in ndindex(Ni):
            for kk in ndindex(Nk):
                f = func1d(arr[ii+s_[:,]+kk])
                Nj = f.shape
                for jj in ndindex(Nj):
                    out[ii+jj+kk] = f[jj]

    Equivalently, eliminating the inner loop, this can be expressed as::

        Ni, Nk = a.shape[:axis], a.shape[axis+1:]
        for ii in ndindex(Ni):
            for kk in ndindex(Nk):
                out[ii+s_[...,]+kk] = func1d(arr[ii+s_[:,]+kk])

    Args:
        func1d:
            This function should accept 1-D arrays. It is applied to 1-D slices
            of `arr` along the specified axis.
        axis:
            Axis along which `arr` is sliced.
        arr:
            Input array.
        args:
            Additional arguments to `func1d`.
        kwargs:
            Additional named arguments to `func1d`.

    Returns:
        The output array. The shape of `out` is identical to the shape of
        `arr`, except along the `axis` dimension. This axis is removed, and
        replaced with new dimensions equal to the shape of the return value of
        `func1d`. So if `func1d` returns a scalar `out` will have one fewer
        dimensions than `arr`.

    Examples:
        >>> q0, q1 = numpoly.variable(2)
        >>> b = numpoly.polynomial([[1, 2, 3*q0],
        ...                         [3, 6*q1, 6],
        ...                         [2, 7, 9]])
        >>> numpoly.apply_along_axis(numpoly.mean, 0, b)
        polynomial([2.0, 2.0*q1+3.0, q0+5.0])
        >>> numpoly.apply_along_axis(numpoly.mean, 1, b)
        polynomial([q0+1.0, 2.0*q1+3.0, 6.0])

    """
    collection: List[ndpoly] = list()

    @wraps(func1d)
    def wrapper_func(array):
        """Wrap func1d function."""
        # Align indeterminants in case slicing changed them
        array = numpoly.polynomial(array,
                                   names=arr.indeterminants,
                                   allocation=arr.allocation)
        array, _ = numpoly.align.align_indeterminants(array,
                                                      arr.indeterminants)

        # Evaluate function
        out = func1d(array, *args, **kwargs)

        # Restore indeterminants in case func1d changed them.
        out, _ = numpoly.align.align_indeterminants(out, arr.indeterminants)

        # Return dummy index integer value that will be replaced with
        # polynomials afterwards.
        ret_val = len(collection) * numpy.ones(out.shape, dtype=int)
        collection.append(out)
        return ret_val

    # Initiate wrapper
    arr = numpoly.aspolynomial(arr)
    out = numpy.apply_along_axis(wrapper_func, axis=axis, arr=arr.values)

    # align exponents
    polynomials = numpoly.align.align_exponents(*collection)
    dtype = numpoly.result_type(*polynomials)

    # Store results into new array
    ret_val = numpoly.ndpoly(
        exponents=polynomials[0].exponents,
        shape=out.shape,
        names=polynomials[0].indeterminants,
        dtype=dtype,
    ).values
    for idx, polynomial in enumerate(polynomials):
        ret_val[out == idx] = polynomial.values

    return numpoly.polynomial(
        ret_val,
        dtype=dtype,
        names=polynomials[0].indeterminants,
        allocation=polynomials[0].allocation,
    )
示例#20
0
def true_divide(
    x1: PolyLike,
    x2: PolyLike,
    out: Optional[ndpoly] = None,
    where: numpy.typing.ArrayLike = True,
    **kwargs: Any,
) -> ndpoly:
    """
    Return true division of the inputs, element-wise.

    Instead of the Python traditional 'floor division', this returns a true
    division.  True division adjusts the output type to present the best
    answer, regardless of input types.

    Args:
        x1:
            Dividend array.
        x2:
            Divisor array. 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:
        This is a scalar if both `x1` and `x2` are scalars.

    Raises:
        numpoly.baseclass.FeatureNotSupported:
            If `x2` contains indeterminants, numerical division is no longer
            possible and an error is raised instead. For polynomial
            division see ``numpoly.poly_divide``.

    Examples:
        >>> q0q1q2 = numpoly.variable(3)
        >>> numpoly.true_divide(q0q1q2, 4)
        polynomial([0.25*q0, 0.25*q1, 0.25*q2])
        >>> numpoly.true_divide(q0q1q2, [1, 2, 4])
        polynomial([q0, 0.5*q1, 0.25*q2])

    """
    x1, x2 = numpoly.align_polynomials(x1, x2)
    if not x2.isconstant():
        raise numpoly.FeatureNotSupported(DIVIDE_ERROR_MSG)
    x2 = x2.tonumpy()
    if out is None:
        out_ = numpoly.ndpoly(
            exponents=x1.exponents,
            shape=x1.shape,
            names=x1.indeterminants,
            dtype=numpy.common_type(x1, numpy.array(1.)),
        )
    else:
        assert len(out) == 1
        out_ = out[0]
    assert isinstance(out_, numpoly.ndpoly)
    for key in x1.keys:
        out_[key] = 0
        numpy.true_divide(x1.values[key],
                          x2,
                          out=out_.values[key],
                          where=numpy.asarray(where),
                          **kwargs)
    if out is None:
        out_ = numpoly.clean_attributes(out_)
    return out_
示例#21
0
def full_like(
    a: PolyLike,
    fill_value: numpy.typing.ArrayLike,
    dtype: Optional[numpy.typing.DTypeLike] = None,
    order: str = "K",
    subok: bool = True,
    shape: Optional[Sequence[int]] = None,
) -> ndpoly:
    """
    Return a full array with the same shape and type as a given array.

    Args:
        a:
            The shape and data-type of `a` define these same attributes of
            the returned array.
        fill_value:
            Fill value. Must be broadcast compatible with shape.
        dtype:
            Overrides the data type of the result.
        order:
            Overrides the memory layout of the result. 'C' means C-order,
            'F' means F-order, 'A' means 'F' if `a` is Fortran contiguous,
            'C' otherwise. 'K' means match the layout of `a` as closely
            as possible.
        subok:
            If True, then the newly created array will use the sub-class
            type of 'a', otherwise it will be a base-class array. Defaults
            to True.
        shape:
            Overrides the shape of the result. If order='K' and the number of
            dimensions is unchanged, will try to keep order, otherwise,
            order='C' is implied.

    Returns:
        Array of `fill_value` with the same shape and type as `a`.

    Examples:
        >>> poly = numpoly.monomial(3)
        >>> poly
        polynomial([1, q0, q0**2])
        >>> q0, q1 = numpoly.variable(2)
        >>> numpoly.full_like(poly, q1-1.)
        polynomial([q1-1, q1-1, q1-1])

    """
    del subok
    if not isinstance(a, numpy.ndarray):
        a = numpoly.polynomial(a)
    fill_value = numpoly.aspolynomial(fill_value)
    if shape is None:
        shape = a.shape
    if dtype is None:
        dtype = a.dtype
    if order in ("A", "K"):
        order = "F" if a.flags["F_CONTIGUOUS"] else "C"
    out = numpoly.ndpoly(
        exponents=fill_value.exponents,
        shape=tuple(shape),
        names=fill_value.indeterminants,
        dtype=dtype,
        order=order,
    )
    for key in fill_value.keys:
        out.values[key] = fill_value.values[key]
    return out
示例#22
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,
    )
示例#23
0
def multiply(x1, x2, out=None, where=True, **kwargs):
    """
    Multiply arguments element-wise.

    Args:
        x1, x2 (numpoly.ndpoly):
            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 (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 product of `x1` and `x2`, element-wise. This is a scalar if
            both `x1` and `x2` are scalars.

    Examples:
        >>> x1 = numpy.arange(9.0).reshape((3, 3))
        >>> xyz = numpoly.symbols("x y z")
        >>> numpoly.multiply(x1, xyz)
        polynomial([[0.0, y, 2.0*z],
                    [3.0*x, 4.0*y, 5.0*z],
                    [6.0*x, 7.0*y, 8.0*z]])

    """
    x1, x2 = numpoly.align_polynomials(x1, x2)
    no_output = out is None
    if no_output:
        exponents = (numpy.tile(x1.exponents, (len(x2.exponents), 1)) +
                     numpy.repeat(x2.exponents, len(x1.exponents), 0))
        out = numpoly.ndpoly(
            exponents=numpy.unique(exponents, axis=0),
            shape=x1.shape,
            names=x1.indeterminants,
            dtype=x1.dtype,
        )

    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).flatten()
            key = key.view("U%d" % len(expon1)).item()
            if key in seen:
                out[key] += numpy.multiply(coeff1,
                                           coeff2,
                                           where=where,
                                           **kwargs)
            else:
                numpy.multiply(coeff1,
                               coeff2,
                               out=out[key],
                               where=where,
                               **kwargs)
            seen.add(key)
    if no_output:
        out = numpoly.clean_attributes(out)
    return out