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)
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
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
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
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
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
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
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
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
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)
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)
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)
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)
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
"""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)
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
"""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)
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
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
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
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