Beispiel #1
0
def Var(poly, dist=None, **kws):
    """
    Element by element 2nd order statistics.

    Args:
        poly (numpoly.ndpoly, Distribution):
            Input to take variance on.
        dist (Distribution):
            Defines the space the variance is taken on. It is ignored if
            ``poly`` is a distribution.

    Returns:
        (numpy.ndarray):
            Element for element variance along ``poly``, where
            ``variation.shape == poly.shape``.

    Examples:
        >>> dist = chaospy.J(chaospy.Gamma(1, 1), chaospy.Normal(0, 2))
        >>> chaospy.Var(dist)
        array([1., 4.])
        >>> q0, q1 = chaospy.variable(2)
        >>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1])
        >>> chaospy.Var(poly, dist)
        array([  0.,   1.,   4., 800.])
    """
    if dist is None:
        dist, poly = poly, numpoly.variable(len(poly))
    poly = numpoly.set_dimensions(poly, len(dist))
    if not poly.isconstant:

        return poly.tonumpy()**2
    poly = poly-E(poly, dist, **kws)
    poly = numpoly.square(poly)
    return E(poly, dist, **kws)
Beispiel #2
0
def analytical_stieltjes(order, dist, multiplier=1):
    """Analytical Stieltjes' method"""
    dimensions = len(dist)
    mom_order = numpy.arange(order + 1).repeat(dimensions)
    mom_order = mom_order.reshape(order + 1, dimensions).T
    coeffs = dist.ttr(mom_order)
    coeffs[1, :, 0] = 1.0
    orders = numpy.arange(order, dtype=int)
    multiplier, orders = numpy.broadcast_arrays(multiplier, orders)

    var = numpoly.variable(dimensions)
    orth = [numpy.zeros(dimensions), numpy.ones(dimensions)]
    for order_, multiplier_ in zip(orders, multiplier):
        orth.append(
            multiplier_
            * (
                (var - coeffs[0, :, order_]) * orth[-1]
                - coeffs[1, :, order_] * orth[-2]
            )
        )
    orth = numpoly.polynomial(orth[1:]).T
    norms = numpy.cumprod(coeffs[1], 1)

    assert coeffs.shape == (2, dimensions, order + 1)
    assert orth.shape == (len(dist), order + 1)
    assert norms.shape == (len(dist), order + 1)
    return coeffs, orth, norms
Beispiel #3
0
 def _mom(self, kloc, dist, scale, shift, parameters):
     del parameters
     poly = numpoly.variable(len(self))
     poly = numpoly.sum(scale*poly, axis=-1)+shift
     poly = numpoly.set_dimensions(numpoly.prod(poly**kloc), len(self))
     out = sum(dist._get_mom(key)*coeff
               for key, coeff in zip(poly.exponents, poly.coefficients))
     return out
Beispiel #4
0
    def _mom(self, kloc, mean, sigma, cache):
        poly = numpoly.variable(len(self))
        cholesky = numpy.linalg.cholesky(self._covariance)
        poly = numpoly.sum(cholesky * poly, axis=-1) + mean

        poly = numpoly.set_dimensions(numpoly.prod(poly**kloc), len(self))
        out = sum(
            self._dist.mom(key) * coeff
            for key, coeff in zip(poly.exponents, poly.coefficients))
        return out
Beispiel #5
0
def test_result_type(func_interface):
    """Tests for numpoly.result_type."""
    dtypes = ["uint8", "int16", "float32", "complex64"]
    dtypes = [numpy.dtype(dtype) for dtype in dtypes]
    for idx, dtype1 in enumerate(dtypes):
        for dtype2 in dtypes[idx:]:
            assert func_interface.result_type(3, dtype1, dtype2) == dtype2
            assert func_interface.result_type(
                numpoly.variable(dtype=dtype1),
                numpy.arange(3, dtype=dtype2),
            ) == dtype2
Beispiel #6
0
def Spearman(poly, dist=None, sample=10000, retall=False, **kws):
    """
    Calculate Spearman's rank-order correlation coefficient.

    Args:
        poly (numpoly.ndpoly):
            Polynomial of interest.
        dist (Distribution):
            Defines the space where correlation is taken.
        sample (int):
            Number of samples used in estimation.
        retall (bool):
            If true, return p-value as well.

    Returns:
        (float, numpy.ndarray):
            Correlation output ``rho``. Of type float if two-dimensional problem.
            Correleation matrix if larger.
        (float, numpy.ndarray):
            The two-sided p-value for a hypothesis test whose null hypothesis
            is that two sets of data are uncorrelated, has same dimension as
            ``rho``.

    Examples:
        >>> distribution = chaospy.MvNormal(
        ...     [3, 4], [[2, .5], [.5, 1]])
        >>> corr, pvalue = chaospy.Spearman(distribution, sample=50, retall=True)
        >>> corr.round(4)
        array([[1.   , 0.603],
               [0.603, 1.   ]])
        >>> pvalue.round(8)
        array([[0.00e+00, 3.58e-06],
               [3.58e-06, 0.00e+00]])

    """
    if isinstance(poly, chaospy.Distribution):
        poly, dist = numpoly.variable(len(poly)), poly
    else:
        poly = numpoly.polynomial(poly)
    samples = dist.sample(sample, **kws)
    corr = numpy.eye(len(poly))
    pval = numpy.zeros((len(poly), len(poly)))
    evals = poly.ravel()(*samples)
    assert len(poly) == len(evals)
    for idx in range(len(poly)):
        for idy in range(idx + 1, len(poly)):
            if idx == idy:
                pass
            spear = spearmanr(evals[idx], evals[idy])
            pval[idx, idy] = pval[idy, idx] = spear.pvalue
            corr[idx, idy] = corr[idy, idx] = spear.correlation
    if retall:
        return corr, pval
    return corr
Beispiel #7
0
def analytical_stieljes(order, dist, normed=False):
    """
    Examples:
        >>> dist = chaospy.J(chaospy.Uniform(0, 1), chaospy.Beta(3, 4))
        >>> coeffs, orth, norms = analytical_stieljes(2, dist)
        >>> coeffs.round(5)
        array([[[0.5    , 0.5    , 0.5    ],
                [0.42857, 0.46032, 0.47475]],
        <BLANKLINE>
               [[1.     , 0.08333, 0.06667],
                [1.     , 0.03061, 0.04321]]])
        >>> orth[:, 2].round(5)
        polynomial([q0**2-q0+0.16667, q1**2-0.88889*q1+0.16667])
        >>> norms.round(5)
        array([[1.     , 0.08333, 0.00556],
               [1.     , 0.03061, 0.00132]])
        >>> coeffs, orth, norms = analytical_stieljes(2, dist, normed=True)
        >>> coeffs.round(5)
        array([[[0.5    , 0.5    , 0.5    ],
                [0.42857, 0.46032, 0.47475]],
        <BLANKLINE>
               [[1.     , 0.08333, 0.06667],
                [1.     , 0.03061, 0.04321]]])
        >>> orth[:, 2].round(5)
        polynomial([13.41641*q0**2-13.41641*q0+2.23607,
                    27.49545*q1**2-24.4404*q1+4.58258])
        >>> norms.round(5)
        array([[1., 1., 1.],
               [1., 1., 1.]])
    """
    dimensions = len(dist)
    mom_order = numpy.arange(order + 1).repeat(dimensions)
    mom_order = mom_order.reshape(order + 1, dimensions).T
    coeffs = dist.ttr(mom_order)
    coeffs[1, :, 0] = 1.

    var = numpoly.variable(dimensions)
    orth = [numpy.zeros(dimensions), numpy.ones(dimensions)]
    for order_ in range(order):
        orth.append((var - coeffs[0, :, order_]) * orth[-1] -
                    coeffs[1, :, order_] * orth[-2])
    orth = numpoly.polynomial(orth[1:]).T

    norms = numpy.cumprod(coeffs[1], 1)
    if normed:
        orth = numpoly.true_divide(orth, numpy.sqrt(norms))
        norms **= 0

    return coeffs, orth, norms
Beispiel #8
0
def Kurt(poly, dist=None, fisher=True, **kws):
    """
    The forth order statistical moment Kurtosis.

    Element by element 4rd order statistics of a distribution or polynomial.

    Args:
        poly (numpoly.ndpoly, Distribution):
            Input to take kurtosis on.
        dist (Distribution):
            Defines the space the skewness is taken on. It is ignored if
            ``poly`` is a distribution.
        fisher (bool):
            If True, Fisher's definition is used (Normal -> 0.0). If False,
            Pearson's definition is used (normal -> 3.0)

    Returns:
        (numpy.ndarray):
            Element for element variance along ``poly``, where
            ``skewness.shape==poly.shape``.

    Examples:
        >>> dist = chaospy.J(chaospy.Gamma(1, 1), chaospy.Normal(0, 2))
        >>> chaospy.Kurt(dist).round(4)
        array([6., 0.])
        >>> chaospy.Kurt(dist, fisher=False).round(4)
        array([9., 3.])
        >>> q0, q1 = chaospy.variable(2)
        >>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1])
        >>> chaospy.Kurt(poly, dist).round(4)
        array([nan,  6.,  0., 15.])
        >>> chaospy.Kurt(4., dist)
        array(nan)

    """
    adjust = 3 if fisher else 0

    if dist is None:
        dist, poly = poly, numpoly.variable(len(poly))
    poly = numpoly.set_dimensions(poly, len(dist))
    if poly.isconstant():
        return numpy.full(poly.shape, numpy.nan)

    poly = poly - E(poly, dist, **kws)
    poly = numpoly.true_divide(poly, Std(poly, dist, **kws))
    return E(poly**4, dist, **kws) - adjust
Beispiel #9
0
def E(poly, dist=None, **kws):
    """
    The expected value of a distribution or polynomial.

    1st order statistics of a probability distribution or polynomial on a given
    probability space.

    Args:
        poly (numpoly.ndpoly, Distribution):
            Input to take expected value on.
        dist (Distribution):
            Defines the space the expected value is taken on. It is ignored if
            ``poly`` is a distribution.

    Returns:
        (numpy.ndarray):
            The expected value of the polynomial or distribution, where
            ``expected.shape == poly.shape``.

    Examples:
        >>> dist = chaospy.J(chaospy.Gamma(1, 1), chaospy.Normal(0, 2))
        >>> chaospy.E(dist)
        array([1., 0.])
        >>> q0, q1 = chaospy.variable(2)
        >>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1])
        >>> chaospy.E(poly, dist)
        array([ 1.,  1.,  0., -1.])

    """
    if dist is None:
        dist, poly = poly, numpoly.variable(len(poly))

    poly = numpoly.set_dimensions(poly, len(dist))
    if poly.isconstant():
        return poly.tonumpy()

    moments = dist.mom(poly.exponents.T, **kws)
    if len(dist) == 1:
        moments = moments[0]

    out = numpy.zeros(poly.shape)
    for idx, key in enumerate(poly.keys):
        out += poly[key] * moments[idx]
    return out
Beispiel #10
0
def Corr(poly, dist=None, **kws):
    """
    Correlation matrix of a distribution or polynomial.

    Args:
        poly (numpoly.ndpoly, Distribution):
            Input to take correlation on. Must have ``len(poly)>=2``.
        dist (Distribution):
            Defines the space the correlation is taken on.  It is ignored if
            ``poly`` is a distribution.

    Returns:
        (numpy.ndarray):
            Correlation matrix with
            ``correlation.shape == poly.shape+poly.shape``.

    Examples:
        >>> distribution = chaospy.MvNormal(
        ...     [3, 4], [[2, .5], [.5, 1]])
        >>> chaospy.Corr(distribution).round(4)
        array([[1.    , 0.3536],
               [0.3536, 1.    ]])
        >>> q0 = chaospy.variable()
        >>> poly = chaospy.polynomial([q0, q0**2])
        >>> distribution = chaospy.Normal()
        >>> chaospy.Corr(poly, distribution).round(4)
        array([[1., 0.],
               [0., 1.]])

    """
    if isinstance(poly, chaospy.Distribution):
        poly, dist = numpoly.variable(len(poly)), poly
    else:
        poly = numpoly.polynomial(poly)

    if not poly.shape:
        return numpy.ones((1, 1))

    cov = chaospy.Cov(poly, dist, **kws)
    var = numpy.diag(cov)
    vvar = numpy.sqrt(numpy.outer(var, var))
    return numpy.where(vvar > 0, cov / vvar, 0)
Beispiel #11
0
def Cov(poly, dist=None, **kws):
    """
    Variance/Covariance matrix of a distribution or polynomial array.

    Args:
        poly (numpoly.ndpoly, Distribution) :
            Input to take covariance on. Must have `len(poly)>=2`.
        dist (Distribution) :
            Defines the space the covariance is taken on.  It is ignored if
            `poly` is a distribution.

    Returns:
        (numpy.ndarray):
            Covariance matrix with shape ``poly.shape+poly.shape``.

    Examples:
        >>> dist = chaospy.MvNormal([0, 0], [[2, .5], [.5, 1]])
        >>> chaospy.Cov(dist)
        array([[2. , 0.5],
               [0.5, 1. ]])
        >>> q0, q1 = chaospy.variable(2)
        >>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1])
        >>> chaospy.Cov(poly, dist)
        array([[  0. ,   0. ,   0. ,   0. ],
               [  0. ,   2. ,   0.5,   0. ],
               [  0. ,   0.5,   1. ,   0. ],
               [  0. ,   0. ,   0. , 225. ]])
        >>> chaospy.Cov([1, 2, 3], dist)
        array([[0., 0., 0.],
               [0., 0., 0.],
               [0., 0., 0.]])

    """
    if dist is None:
        dist, poly = poly, numpoly.variable(len(poly))
    poly = numpoly.set_dimensions(poly, len(dist))
    if poly.isconstant():
        return numpy.zeros((len(poly), len(poly)))
    poly = poly-E(poly, dist)
    poly = numpoly.outer(poly, poly)
    return E(poly, dist)
Beispiel #12
0
def polynomial_from_roots(
    seq_of_zeros: Sequence[int],
    dtype: Optional[numpy.typing.DTypeLike] = None,
) -> ndpoly:
    """
    Find the coefficients of a polynomial with the given sequence of roots.

    Returns the coefficients of the polynomial whose leading coefficient is one
    for the given sequence of zeros (multiple roots must be included in the
    sequence as many times as their multiplicity; see Examples). A square
    matrix (or array, which will be treated as a matrix) can also be given, in
    which case the coefficients of the characteristic polynomial of the matrix
    are returned.

    Args:
        seq_of_zeros:
            A sequence of polynomial roots, or a square array or matrix object.
            Either shape (N,) or (N, N).
        dtype:
            Any object that can be interpreted as a numpy data type.

    Returns:
        1-D polynomial which have `seq_of_zeros` as roots.
        Leading coefficient is always 1.

    Raises:
        ValueError:
            If input is the wrong shape (the input must be a 1-D or square
            2-D array).

    Examples:
        >>> numpoly.polynomial_from_roots((0, 0, 0))
        polynomial(q0**3)
        >>> numpoly.polynomial_from_roots((-0.5, 0, 0.5))
        polynomial(q0**3-0.25*q0)

    """
    exponent = numpy.arange(len(seq_of_zeros), -1, -1, dtype=int)
    basis = numpoly.variable(dtype=dtype)**exponent
    return numpoly.sum(numpy.poly(seq_of_zeros)*basis)
Beispiel #13
0
def Skew(poly, dist=None, **kws):
    """
    The third order statistical moment Kurtosis.

    Element by element 3rd order statistics of a distribution or polynomial.

    Args:
        poly (numpoly.ndpoly, Distribution):
            Input to take skewness on.
        dist (Distribution):
            Defines the space the skewness is taken on. It is ignored if
            ``poly`` is a distribution.

    Returns:
        (numpy.ndarray):
            Element for element variance along ``poly``, where
            ``skewness.shape == poly.shape``.

    Examples:
        >>> dist = chaospy.J(chaospy.Gamma(1, 1), chaospy.Normal(0, 2))
        >>> chaospy.Skew(dist)
        array([2., 0.])
        >>> q0, q1 = chaospy.variable(2)
        >>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1])
        >>> chaospy.Skew(poly, dist)
        array([nan,  2.,  0.,  0.])
        >>> chaospy.Skew(2., dist)
        array(nan)

    """
    if dist is None:
        dist, poly = poly, numpoly.variable(len(poly))
    poly = numpoly.set_dimensions(poly, len(dist))
    if poly.isconstant():
        return numpy.full(poly.shape, numpy.nan)

    poly = poly - E(poly, dist, **kws)
    poly = numpoly.true_divide(poly, Std(poly, dist, **kws))
    return E(poly**3, dist, **kws)
Beispiel #14
0
def orth_chol(order,
              dist,
              normed=False,
              graded=True,
              reverse=True,
              cross_truncation=1.,
              retall=False,
              **kws):
    """
    Create orthogonal polynomial expansion from Cholesky decomposition.

    Args:
        order (int):
            Order of polynomial expansion
        dist (Dist):
            Distribution space where polynomials are orthogonal
        normed (bool):
            If True orthonormal polynomials will be used instead of monic.
        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.
        cross_truncation (float):
            Use hyperbolic cross truncation scheme to reduce the number of
            terms in expansion.
        retall (bool):
            If true return numerical stabilized norms as well. Roughly the same
            as ``cp.E(orth**2, dist)``.

    Examples:
        >>> distribution = chaospy.Normal()
        >>> expansion, norms = chaospy.orth_chol(3, distribution, retall=True)
        >>> expansion.round(4)
        polynomial([1.0, q0, q0**2-1.0, q0**3-3.0*q0])
        >>> norms
        array([1., 1., 2., 6.])

    """
    dim = len(dist)
    basis = numpoly.monomial(
        start=1,
        stop=order + 1,
        names=numpoly.variable(dim).names,
        graded=graded,
        reverse=reverse,
        cross_truncation=cross_truncation,
    )
    length = len(basis)

    covariance = chaospy.descriptives.Cov(basis, dist)
    cholmat = chaospy.chol.gill_king(covariance)
    cholmat_inv = numpy.linalg.inv(cholmat.T).T
    if not normed:
        diag_mesh = numpy.repeat(numpy.diag(cholmat_inv), len(cholmat_inv))
        cholmat_inv /= diag_mesh.reshape(cholmat_inv.shape)
        norms = numpy.hstack([1, numpy.diag(cholmat)**2])
    else:
        norms = numpy.ones(length + 1, dtype=float)

    coefs = numpy.empty((length + 1, length + 1))

    coefs[1:, 1:] = cholmat_inv
    coefs[0, 0] = 1
    coefs[0, 1:] = 0

    expected = -numpy.sum(
        cholmat_inv * chaospy.descriptives.E(basis, dist, **kws), -1)
    coefs[1:, 0] = expected

    coefs = coefs.T

    out = {}
    out[(0, ) * dim] = coefs[0]
    for idx, key in enumerate(basis.exponents):
        out[tuple(key)] = coefs[idx + 1]

    names = numpoly.symbols("q:%d" % dim)
    polynomials = numpoly.polynomial(out, names=names)

    if retall:
        return polynomials, norms
    return polynomials
Beispiel #15
0
"""Test ndpoly baseclass functionality."""
import numpy
from pytest import raises
import numpoly

XY = numpoly.variable(2)
X, Y = numpoly.symbols("q0"), numpoly.symbols("q1")
EMPTY = numpoly.polynomial([])


def test_scalars():
    """Test scalar objects to catch edgecases."""
    assert XY.shape == (2, )
    assert XY.size == 2
    assert X.shape == ()
    assert X.size == 1
    assert EMPTY.shape in [(), (0, )]  # different behavior in py2/3
    assert EMPTY.size == 0

    assert numpy.all(numpy.array(XY.coefficients) == [[1, 0], [0, 1]])
    assert X.coefficients == [1]
    assert EMPTY.coefficients == []

    assert numpy.all(XY.exponents == [[1, 0], [0, 1]])
    assert XY.exponents.shape == (2, 2)
    assert X.exponents == 1
    assert X.exponents.shape == (1, 1)
    assert numpy.all(EMPTY.exponents == 0)
    assert EMPTY.exponents.shape == (1, 1)

    assert numpy.all(XY.indeterminants == XY)
Beispiel #16
0
def discretized_stieltjes(
    order,
    dist,
    rule=None,
    tolerance=1e-16,
    scaling=3,
    n_max=5000,
):
    """
    Discretized Stieltjes' method.

    Examples:
        >>> dist = chaospy.J(chaospy.Uniform(0, 1), chaospy.Beta(3, 4))
        >>> (alpha, beta), orth, norms = chaospy.discretized_stieltjes(2, dist)
        >>> alpha.round(5)
        array([[0.5    , 0.5    , 0.5    ],
               [0.42857, 0.46032, 0.47475]])
        >>> beta.round(5)
        array([[1.     , 0.08333, 0.06667],
               [1.     , 0.03061, 0.04321]])
        >>> orth[:, 2].round(5)
        polynomial([q0**2-q0+0.16667, q1**2-0.88889*q1+0.16667])
        >>> norms.round(5)
        array([[1.     , 0.08333, 0.00556],
               [1.     , 0.03061, 0.00132]])

    """
    if len(dist) > 1:
        assert not dist.stochastic_dependent
        coeffs, orths, norms = zip(*[
            discretized_stieltjes(
                order, dist_, rule=rule, tolerance=tolerance, scaling=scaling)
            for dist_ in dist
        ])
        coeffs = numpy.dstack(coeffs).reshape(2, len(dist), order + 1)
        variables = list(numpoly.variable(len(dist)))
        orths = [orths[idx](q0=variables[idx]) for idx in range(len(dist))]
        orths = numpoly.polynomial(orths).reshape(len(dist), order + 1)
        norms = numpy.asfarray(norms).reshape(len(dist), order + 1)
        return coeffs, orths, norms

    if rule is None:
        rule = "discrete" if dist.interpret_as_integer else "clenshaw_curtis"
    order_ = (2 * order - 1.) / scaling
    beta = beta_old = numpy.nan
    var = numpoly.variable()
    orths = [numpoly.polynomial(0.), numpoly.polynomial(1.)] + [None] * order
    norms = numpy.ones(order + 2)
    coeffs = numpy.ones((2, order + 1))

    while not numpy.all(numpy.abs(coeffs[1] - beta_old) < tolerance):

        beta_old = coeffs[1].copy()
        order_ = max(order_ * scaling, order_ + 1)
        if order_ > n_max:
            break

        [abscissas], weights = chaospy.generate_quadrature(int(order_),
                                                           dist,
                                                           rule=rule,
                                                           segments=0)
        inner = numpy.sum(abscissas * weights)
        for idx in range(order):
            coeffs[0, idx] = inner / norms[idx + 1]
            coeffs[1, idx] = norms[idx + 1] / norms[idx]
            orths[idx + 2] = ((var - coeffs[0, idx]) * orths[idx + 1] -
                              orths[idx] * coeffs[1, idx])
            norms[idx + 2] = numpy.sum(orths[idx + 2](abscissas)**2 * weights)
            inner = numpy.sum(abscissas * orths[idx + 2](abscissas)**2 *
                              weights)
        coeffs[:, order] = (inner / norms[-1], norms[-1] / norms[-2])

    coeffs = coeffs.reshape(2, 1, order + 1)
    orths = numpoly.polynomial(orths[1:]).reshape(1, order + 1)
    norms = numpy.array(norms[1:]).reshape(1, order + 1)
    return coeffs, orths, norms
Beispiel #17
0
"""Testing saving to and loading from disk."""
from tempfile import TemporaryFile
import pickle
import pytest

import numpy
import numpoly


X, Y, Z = numpoly.variable(3)
ARRAY = numpy.array([1, 2, 3])
POLY = numpoly.polynomial([1, X, Z**2-1])


def test_save(func_interface):
    outfile = TemporaryFile()
    func_interface.save(outfile, X)
    func_interface.save(outfile, ARRAY)
    func_interface.save(outfile, POLY)
    outfile.seek(0)
    assert numpy.all(numpoly.load(outfile) == X)
    assert numpy.all(numpoly.load(outfile) == ARRAY)
    assert numpy.all(numpoly.load(outfile) == POLY)

    with open("/tmp/numpoly_save.npy", "wb") as dst:
        func_interface.save(dst, X)
        func_interface.save(dst, ARRAY)
        func_interface.save(dst, POLY)
    with open("/tmp/numpoly_save.npy", "rb") as src:
        assert numpoly.load(src) == X
        assert numpy.all(numpoly.load(src) == ARRAY)
Beispiel #18
0
def orth_gs(order,
            dist,
            normed=False,
            graded=True,
            reverse=True,
            retall=False,
            cross_truncation=1.,
            **kws):
    """
    Gram-Schmidt process for generating orthogonal polynomials.

    Args:
        order (int, numpoly.ndpoly):
            The upper polynomial order. Alternative a custom polynomial basis
            can be used.
        dist (Dist):
            Weighting distribution(s) defining orthogonality.
        normed (bool):
            If True orthonormal polynomials will be used instead of monic.
        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.

    Returns:
        (chapspy.poly.ndpoly):
            The orthogonal polynomial expansion.

    Examples:
        >>> distribution = chaospy.J(chaospy.Normal(), chaospy.Normal())
        >>> polynomials, norms = chaospy.orth_gs(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_gs(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])

    """
    logger = logging.getLogger(__name__)
    dim = len(dist)

    if isinstance(order, int):
        if order == 0:
            return numpoly.polynomial(1)
        basis = numpoly.monomial(
            0,
            order + 1,
            names=numpoly.variable(2).names,
            graded=graded,
            reverse=reverse,
            cross_truncation=cross_truncation,
        )
    else:
        basis = order

    basis = list(basis)

    polynomials = [basis[0]]

    norms = [1.]
    for idx in range(1, len(basis)):

        # orthogonalize polynomial:
        for idy in range(idx):
            orth = chaospy.E(basis[idx] * polynomials[idy], dist, **kws)
            basis[idx] = basis[idx] - polynomials[idy] * orth / norms[idy]

        norms_ = chaospy.E(basis[idx]**2, dist, **kws)
        if norms_ <= 0:
            logger.warning("Warning: Polynomial cutoff at term %d", idx)
            break

        norms.append(1. if normed else norms_)
        basis[idx] = basis[idx] / numpy.sqrt(norms_) if normed else basis[idx]
        polynomials.append(basis[idx])

    polynomials = chaospy.polynomial(polynomials).flatten()
    if retall:
        norms = numpy.array(norms)
        return polynomials, norms
    return polynomials
Beispiel #19
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
Beispiel #20
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([-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(4)
        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")

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

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

    if size == 1:
        out = numpoly.monomial(
            0, 1, names=names, 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
Beispiel #21
0
def discretized_stieltjes(order, abscissas, weights, normed=False):
    """
    Discretized Stieltjes' method.

    Args:
        order (int):
            The polynomial order create.
        abscissas (numpy.ndarray):
            Quadrature abscissas, assumed to be of ``shape == (D, N)``, where
            ``D`` is the number of distributions to handle at once, and ``N``
            is the number of abscissas.
        weights (numpy.ndarray):
            Quadrature weights, assumed to be of ``shape == (N,)``.
        normed (bool):
            If true, normalize polynomials.

    Returns:
        (numpy.ndarray, numpy.ndarray, numpy.ndarray):
            coefficients:
                The recurrence coefficients created using the discretized
                Stieltjes' method, with ``shape == (2, D, order+1)``.
            polynomials:
                The orthogonal polynomial expansion created as a by-product of
                the algorithm.
            norms:
                The norm of each orthogonal polynomial. Roughly equivalent to
                ``chaospy.E(polynomials**2, dist)``, but more numerically
                stable than most alternatives.

    Examples:
        >>> dist = chaospy.J(chaospy.Uniform(0, 1), chaospy.Beta(3, 4))
        >>> abscissas, weights = chaospy.generate_quadrature(
        ...     9, dist, rule="clenshaw_curtis")
        >>> coeffs, orth, norms = discretized_stieltjes(2, abscissas, weights)
        >>> coeffs.round(5)
        array([[[0.5    , 0.5    , 0.5    ],
                [0.42857, 0.46032, 0.47525]],
        <BLANKLINE>
               [[1.     , 0.08333, 0.06667],
                [1.     , 0.03061, 0.04321]]])
        >>> orth[2].round(5)
        polynomial([q0**2-q0+0.16667, q1**2-0.88889*q1+0.16667])
        >>> norms.round(5)
        array([[1.     , 0.08333, 0.00556],
               [1.     , 0.03061, 0.00132]])
        >>> coeffs, orth, norms = discretized_stieltjes(
        ...     2, abscissas, weights, normed=True)
        >>> coeffs.round(5)
        array([[[0.5    , 0.04167, 0.26424],
                [0.42857, 0.01409, 0.31365]],
        <BLANKLINE>
               [[1.     , 1.     , 1.     ],
                [1.     , 1.     , 1.     ]]])
        >>> orth[2].round(5)
        polynomial([3.9155*q0**2-2.1209*q0-1.04874,
                    5.94906*q1**2-2.63343*q1-1.00494])
        >>> norms.round(5)
        array([[1., 1., 1.],
               [1., 1., 1.]])
    """
    abscissas = numpy.asfarray(abscissas)
    weights = numpy.asfarray(weights)
    assert len(weights.shape) == 1
    assert len(abscissas.shape) == 2
    assert abscissas.shape[-1] == len(weights)

    poly = numpoly.variable(len(abscissas))
    orth = [
        numpoly.repeat(0., len(abscissas)),
        numpoly.repeat(1., len(abscissas))
    ]

    inner = numpy.sum(abscissas * weights, -1)
    norms = [numpy.ones(len(abscissas)), numpy.ones(len(abscissas))]
    coeffs = []

    for _ in range(int(order)):

        coeffs.append((inner / norms[-1], norms[-1] / norms[-2]))
        orth.append((poly - coeffs[-1][0]) * orth[-1] -
                    orth[-2] * coeffs[-1][1])

        raw_nodes = orth[-1](*abscissas)**2 * weights
        inner = numpy.sum(abscissas * raw_nodes, -1)
        norms.append(numpy.sum(raw_nodes, -1))

        if normed:
            orth[-1] = orth[-1] / numpy.sqrt(norms[-1])
            norms[-1] **= 0

    coeffs.append((inner / norms[-1], norms[-1] / norms[-2]))

    coeffs = numpy.moveaxis(coeffs, 0, 2)
    norms = numpy.array(norms[1:]).T
    orth = numpoly.polynomial(orth[1:])

    return coeffs, orth, norms