def __init__(self, rules): r""" Initialize a :py:class:`TensorProductQR` instance. :param rules: A list of :py:class:`QuadratureRule` subclass instances. Their nodes and weights will be used to compute the tensor product. """ # The dimension of the quadrature rule. self._dimension = len(rules) # The individual quadrature rules. self._qrs = tuple(rules) # The order R of the tensor product quadrature. self._order = None # TODO: Compute the order from the orders of the input QRs # The number of nodes in this quadrature rule. self._number_nodes = reduce(op.mul, [ rule.get_number_nodes() for rule in rules ]) # The quadrature nodes \gamma. nodes = meshgrid_nd([ rule.get_nodes() for rule in rules ]) self._nodes = vstack([ node.flatten() for node in nodes ]) # The quadrature weights \omega. weights = meshgrid_nd([ rule.get_weights() for rule in rules ]) weights = reduce(lambda x,y: x*y, weights) self._weights = weights.flatten()
def tensor_product(self, rules): r"""Compute the tensor product of the given quadrature rules. :param rules: A list of one dimensional quadrature rules. :return: The nodes :math:`\{\gamma_i\}_i` and weights :math:`\{\omega_i\}_i` of the tensor product quadrature rule. The array of all nodes has a shape of :math:`(D, |\Gamma|)` and the array of weights is of shape :math:`(|\Gamma|)`. """ # The quadrature nodes \gamma. nodes = meshgrid_nd([ rule.get_nodes() for rule in rules ]) nodes = vstack([ node.flatten() for node in nodes ]) # The quadrature weights \omega. weights = meshgrid_nd([ rule.get_weights() for rule in rules ]) weights = reduce(lambda x,y: x*y, weights) return nodes, weights.flatten()
def construct_rule(self, level=None, tolerance=1e-15): r"""Compute the quadrature nodes :math:`\{\gamma_i\}_i` and quadrature weights :math:`\{\omega_i\}_i`. :param level: The level :math:`k` of the Smolyak construction. :param tolerance: Tolerance for dropping identical quadrature nodes. .. warning:: This method is very expensive and may take a long time to finish. Also, the quadrature nodes may use large amounts of memory depending on the dimension and level parameters. """ D = self._dimension if level is None: k = self._level else: k = level if k > max(self._rules.keys()): raise ValueError("Not enough quadrature rules to build Smolyak grid of level "+str(k)) allnodes = [] allweights = [] factors = [] # Index Set for q in xrange(max(0, k-D), k): S = self.enumerate_lattice_points(q) for j, s in enumerate(S): # Only use non-negative nodes for the construction. # The quadrature nodes \gamma. rules = [ self._rules[si+1] for si in s ] nodes = [ rule.get_nodes() for rule in rules ] indices = [ where(n >= 0) for n in nodes ] nodes = meshgrid_nd([ n[i] for n, i in zip(nodes, indices) ]) nodes = vstack([ node.flatten() for node in nodes ]) # The quadrature weights \omega. weights = [ rule.get_weights() for rule in rules ] weights = meshgrid_nd([ w[i] for w, i in zip(weights, indices) ]) weights = reduce(lambda x,y: x*y, weights) allnodes.append(nodes) allweights.append(weights.flatten()) factors.append((-1)**(k-1-q) * binom(D-1, k-1-q)) # Sort allnodes = hstack(allnodes).reshape(D,-1) allweights = hstack([factor*weights for factor, weights in zip(factors,allweights)]).reshape(1,-1) I = lexsort(map(squeeze, vsplit(allnodes, D))[::-1]) allnodes = allnodes[:,I].reshape(D,-1) allweights = allweights[:,I].reshape(1,-1) # Remove duplicates last = 0 I = [last] no = norm(allnodes[:,:-1] - allnodes[:,1:], axis=0) for col in xrange(1, allnodes.shape[1]): if no[col-1] <= tolerance: allweights[0,last] += allweights[0,col] allweights[0,col] = 0 else: last = col I.append(last) allnodes = allnodes[:,I] allweights = allweights[:,I] # Mirror points to all other hyperoctants for d in xrange(D): indices = abs(allnodes[d,:]) >= tolerance mirrorn = allnodes[:,indices] mirrorn[d,:] *= -1.0 mirrorw = allweights[:,indices] allnodes = hstack([allnodes, mirrorn]).reshape(D,-1) allweights = hstack([allweights, mirrorw]).reshape(1,-1) self._nodes = allnodes self._weights = allweights self._number_nodes = allnodes.shape[1]