Ejemplo n.º 1
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
Ejemplo n.º 2
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_)
Ejemplo n.º 3
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
Ejemplo n.º 4
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)
Ejemplo n.º 5
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
Ejemplo n.º 6
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_
Ejemplo n.º 7
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_
Ejemplo n.º 8
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
Ejemplo n.º 9
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