Esempio n. 1
0
def _construct_collection(
    orders,
    dist,
    x_lookup,
    w_lookup,
):
    """Create a collection of {abscissa: weight} key-value pairs."""
    order = numpy.min(orders)
    skew = orders - order

    # Indices and coefficients used in the calculations
    indices = numpoly.glexindex(order - len(dist) + 1,
                                order + 1,
                                dimensions=len(dist))
    coeffs = numpy.sum(indices, -1)
    coeffs = (2 * (
        (order - coeffs + 1) % 2) - 1) * comb(len(dist) - 1, order - coeffs)

    collection = defaultdict(float)
    for bidx, coeff in zip(indices + skew, coeffs.tolist()):
        abscissas = [value[idx] for idx, value in zip(bidx, x_lookup)]
        weights = [value[idx] for idx, value in zip(bidx, w_lookup)]
        for abscissa, weight in zip(product(*abscissas), product(*weights)):
            collection[abscissa] += numpy.prod(weight) * coeff

    return collection
def orth_ttr(order,
             dist,
             normed=False,
             graded=True,
             reverse=True,
             retall=False,
             cross_truncation=1.,
             sort=None,
             **kws):
    """
    Create orthogonal polynomial expansion from three terms recursion formula.

    Args:
        order (int):
            Order of polynomial expansion.
        dist (Dist):
            Distribution space where polynomials are orthogonal If dist.ttr
            exists, it will be used. Must be stochastically independent.
        normed (bool):
            If True orthonormal polynomials will be used.
        graded (bool):
            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**2*q1*q2``,
            ``q0*q1**2*q2`` and ``q0*q1*q2**2``, which all have exponent sum of
            5.
        reverse (bool):
            Reverse lexicographical sorting meaning that ``q0*q1**3`` is
            considered bigger than ``q0**3*q1``, instead of the opposite.
        retall (bool):
            If true return numerical stabilized norms as well. Roughly the same
            as ``cp.E(orth**2, dist)``.
        cross_truncation (float):
            Use hyperbolic cross truncation scheme to reduce the number of
            terms in expansion. only include terms where the exponents ``K``
            satisfied the equation
            ``order >= sum(K**(1/cross_truncation))**cross_truncation``.

    Returns:
        (chaospy.poly.ndpoly, numpy.ndarray):
            Orthogonal polynomial expansion. Norms of the orthogonal
            expansion on the form ``E(orth**2, dist)``. Calculated using
            recurrence coefficients for stability.

    Examples:
        >>> distribution = chaospy.Normal()
        >>> expansion, norms = chaospy.orth_ttr(4, distribution, retall=True)
        >>> expansion.round(4)
        polynomial([1.0, q0, q0**2-1.0, q0**3-3.0*q0, q0**4-6.0*q0**2+3.0])
        >>> norms
        array([ 1.,  1.,  2.,  6., 24.])
    """
    logger = logging.getLogger(__name__)
    if sort is not None:
        logger.warning("deprecation warning: 'sort' argument is deprecated; "
                       "use 'graded' and/or 'reverse' instead")
        graded = "G" in sort.upper()
        reverse = "R" not in sort.upper()

    try:
        _, polynomials, norms, = chaospy.quadrature.recurrence.analytical_stieljes(
            numpy.max(order), dist, normed=normed)
    except NotImplementedError:
        abscissas, weights = chaospy.quadrature.generate_quadrature(
            int(10000**(1 / len(dist))), dist, rule="fejer")
        _, polynomials, norms, = chaospy.quadrature.recurrence.discretized_stieltjes(
            numpy.max(order), abscissas, weights, normed=normed)

    polynomials = polynomials.reshape((len(dist), numpy.max(order) + 1))

    order = numpy.array(order)
    indices = numpoly.glexindex(start=0,
                                stop=order + 1,
                                dimensions=len(dist),
                                graded=graded,
                                reverse=reverse,
                                cross_truncation=cross_truncation)
    if len(dist) > 1:
        polynomials = chaospy.poly.prod(
            chaospy.polynomial(
                [poly[idx] for poly, idx in zip(polynomials, indices.T)]), 0)
        norms = numpy.prod(
            [norms_[idx] for norms_, idx in zip(norms, indices.T)], 0)
    else:
        polynomials = polynomials.flatten()
        norms = norms.flatten()

    if retall:
        return polynomials, norms
    return polynomials
Esempio n. 3
0
def lagrange_polynomial(abscissas, graded=True, reverse=True, sort=None):
    """
    Create Lagrange polynomials.

    Args:
        abscissas (numpy.ndarray):
            Sample points where the Lagrange polynomials shall be defined.
        graded (bool):
            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**2*q1*q2``,
            ``q0*q1**2*q2`` and ``q0*q1*q2**2``, which all have exponent sum of
            5.
        reverse (bool):
            Reverse lexicographical sorting meaning that ``q0*q1**3`` is
            considered bigger than ``q0**3*q1``, instead of the opposite.

    Example:
        >>> chaospy.lagrange_polynomial([4]).round(4)
        polynomial([4.0])
        >>> chaospy.lagrange_polynomial([-10, 10]).round(4)
        polynomial([-0.05*q0+0.5, 0.05*q0+0.5])
        >>> chaospy.lagrange_polynomial([-1, 0, 1]).round(4)
        polynomial([0.5*q0**2-0.5*q0, -q0**2+1.0, 0.5*q0**2+0.5*q0])
        >>> poly = chaospy.lagrange_polynomial([[1, 0, 1], [0, 1, 2]])
        >>> poly.round(4)
        polynomial([-0.5*q1+0.5*q0+0.5, -q0+1.0, 0.5*q1+0.5*q0-0.5])
        >>> poly([1, 0, 1], [0, 1, 2]).round(14)
        array([[1., 0., 0.],
               [0., 1., 0.],
               [0., 0., 1.]])
        >>> nodes = numpy.array([[ 0.17,  0.15,  0.17,  0.19],
        ...                      [14.94, 16.69, 16.69, 16.69]])
        >>> poly = chaospy.lagrange_polynomial(nodes)  # doctest: +IGNORE_EXCEPTION_DETAIL
        Traceback (most recent call last):
            ...
        LinAlgError: Lagrange abscissas resulted in invertible matrix
    """
    abscissas = numpy.asfarray(abscissas)
    if len(abscissas.shape) == 1:
        abscissas = abscissas.reshape(1, abscissas.size)
    dim, size = abscissas.shape

    order = 1
    while comb(order + dim, dim) < size:
        order += 1

    indices = numpoly.glexindex(0,
                                order + 1,
                                dimensions=dim,
                                graded=graded,
                                reverse=reverse)[:size]
    idx, idy = numpy.mgrid[:size, :size]

    matrix = numpy.prod(abscissas.T[idx]**indices[idy], -1)
    det = numpy.linalg.det(matrix)
    if det == 0:
        raise numpy.linalg.LinAlgError(
            "Lagrange abscissas resulted in invertible matrix")

    vec = numpoly.monomial(0,
                           order + 1,
                           dimensions=dim,
                           graded=graded,
                           reverse=reverse)[:size]

    coeffs = numpy.zeros((size, size))

    if size == 1:
        out = numpoly.monomial(
            0, 1, dimensions=dim, graded=graded,
            reverse=reverse) * abscissas.item()

    elif size == 2:
        coeffs = numpy.linalg.inv(matrix)
        out = numpoly.sum(vec * (coeffs.T), 1)

    else:
        for i in range(size):
            if i % 2 != 0:
                k = 1
            else:
                k = 0
            for j in range(size):
                if k % 2 == 0:
                    coeffs[i, j] += numpy.linalg.det(matrix[1:, 1:])
                else:
                    if size % 2 == 0:
                        coeffs[i, j] += -numpy.linalg.det(matrix[1:, 1:])
                    else:
                        coeffs[i, j] += numpy.linalg.det(matrix[1:, 1:])
                matrix = numpy.roll(matrix, -1, axis=0)
                k += 1
            matrix = numpy.roll(matrix, -1, axis=1)
        coeffs /= det
        out = numpoly.sum(vec * (coeffs.T), 1)

    return out
Esempio n. 4
0
def test_glexindex():
    assert not numpoly.glexindex(0).size
    assert numpy.all(numpoly.glexindex(1) == [[0]])
    assert numpy.all(numpoly.glexindex(5) ==
                     [[0], [1], [2], [3], [4]])
    assert numpy.all(numpoly.glexindex(2, dimensions=2) ==
                     [[0, 0], [1, 0], [0, 1]])
    assert numpy.all(numpoly.glexindex(start=2, stop=3, dimensions=2) ==
                     [[2, 0], [1, 1], [0, 2]])
    assert numpy.all(numpoly.glexindex(start=2, stop=[3, 4], dimensions=2) ==
                     [[2, 0], [1, 1], [0, 2], [0, 3]])
    assert numpy.all(numpoly.glexindex(start=[2, 5], stop=[3, 6], dimensions=2) ==
                     [[2, 0], [1, 1], [1, 2], [0, 5]])
    assert numpy.all(numpoly.glexindex(start=2, stop=4, dimensions=2, cross_truncation=0) ==
                     [[2, 0], [3, 0], [0, 2], [0, 3]])
    assert numpy.all(numpoly.glexindex(start=2, stop=4, dimensions=2, cross_truncation=1) ==
                     [[2, 0], [3, 0], [1, 1], [2, 1], [0, 2], [1, 2], [0, 3]])
    assert numpy.all(numpoly.glexindex(start=2, stop=4, dimensions=2, cross_truncation=2) ==
                     [[2, 0], [3, 0], [1, 1], [2, 1], [0, 2], [1, 2], [2, 2], [0, 3]])
    assert numpy.all(numpoly.glexindex(start=0, stop=2, dimensions=3) ==
                     [[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])

    assert numpy.all(numpoly.glexindex(start=0, stop=[1, 1, 1]) == [0, 0, 0])
def orth_ttr(order,
             dist,
             normed=False,
             graded=True,
             reverse=True,
             retall=False,
             cross_truncation=1.):
    """
    Create orthogonal polynomial expansion from three terms recurrence formula.

    Args:
        order (int):
            Order of polynomial expansion.
        dist (Distribution):
            Distribution space where polynomials are orthogonal If dist.ttr
            exists, it will be used. Must be stochastically independent.
        normed (bool):
            If True orthonormal polynomials will be used.
        graded (bool):
            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**2*q1*q2``,
            ``q0*q1**2*q2`` and ``q0*q1*q2**2``, which all have exponent sum of
            5.
        reverse (bool):
            Reverse lexicographical sorting meaning that ``q0*q1**3`` is
            considered bigger than ``q0**3*q1``, instead of the opposite.
        retall (bool):
            If true return numerical stabilized norms as well. Roughly the same
            as ``cp.E(orth**2, dist)``.
        cross_truncation (float):
            Use hyperbolic cross truncation scheme to reduce the number of
            terms in expansion. only include terms where the exponents ``K``
            satisfied the equation
            ``order >= sum(K**(1/cross_truncation))**cross_truncation``.

    Returns:
        (numpoly.ndpoly, numpy.ndarray):
            Orthogonal polynomial expansion. Norms of the orthogonal
            expansion on the form ``E(orth**2, dist)``. Calculated using
            recurrence coefficients for stability.

    Examples:
        >>> distribution = chaospy.J(chaospy.Normal(), chaospy.Normal())
        >>> polynomials, norms = chaospy.orth_ttr(2, distribution, retall=True)
        >>> polynomials.round(10)
        polynomial([1.0, q1, q0, q1**2-1.0, q0*q1, q0**2-1.0])
        >>> norms.round(10)
        array([1., 1., 1., 2., 1., 2.])
        >>> polynomials = chaospy.orth_ttr(2, distribution, normed=True)
        >>> polynomials.round(3)
        polynomial([1.0, q1, q0, 0.707*q1**2-0.707, q0*q1, 0.707*q0**2-0.707])

    """
    _, polynomials, norms, = chaospy.stieltjes(numpy.max(order), dist)
    if normed:
        polynomials = numpoly.true_divide(polynomials, numpy.sqrt(norms))
        norms[:] = 1.

    polynomials = polynomials.reshape((len(dist), numpy.max(order) + 1))

    order = numpy.array(order)
    indices = numpoly.glexindex(start=0,
                                stop=order + 1,
                                dimensions=len(dist),
                                graded=graded,
                                reverse=reverse,
                                cross_truncation=cross_truncation)
    if len(dist) > 1:
        polynomials = numpoly.prod(
            chaospy.polynomial(
                [poly[idx] for poly, idx in zip(polynomials, indices.T)]), 0)
        norms = numpy.prod(
            [norms_[idx] for norms_, idx in zip(norms, indices.T)], 0)
    else:
        polynomials = polynomials.flatten()
        norms = norms.flatten()

    if retall:
        return polynomials, norms
    return polynomials
Esempio n. 6
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