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