def translate_from(self, src_expansion, src_coeff_exprs, src_rscale, dvec, tgt_rscale): logger.info("building translation operator: %s(%d) -> %s(%d): start" % (type(src_expansion).__name__, src_expansion.order, type(self).__name__, self.order)) if not self.use_rscale: src_rscale = 1 tgt_rscale = 1 from sumpy.expansion.multipole import VolumeTaylorMultipoleExpansionBase if isinstance(src_expansion, VolumeTaylorMultipoleExpansionBase): # We know the general form of the multipole expansion is: # # coeff0 * diff(kernel, mi0) + coeff1 * diff(kernel, mi1) + ... # # To get the local expansion coefficients, we take derivatives of # the multipole expansion. # # This code speeds up derivative taking by caching all kernel # derivatives. taker = src_expansion.get_kernel_derivative_taker(dvec) from sumpy.tools import add_mi result = [] for deriv in self.get_coefficient_identifiers(): local_result = [] for coeff, term in zip( src_coeff_exprs, src_expansion.get_coefficient_identifiers()): kernel_deriv = (src_expansion.get_scaled_multipole( taker.diff(add_mi(deriv, term)), dvec, src_rscale, nderivatives=sum(deriv) + sum(term), nderivatives_for_scaling=sum(term))) local_result.append(coeff * kernel_deriv * tgt_rscale**sum(deriv)) result.append(sym.Add(*local_result)) else: from sumpy.tools import MiDerivativeTaker expr = src_expansion.evaluate(src_coeff_exprs, dvec, rscale=src_rscale) taker = MiDerivativeTaker(expr, dvec) # Rscale/operand magnitude is fairly sensitive to the order of # operations--which is something we don't have fantastic control # over at the symbolic level. The '.expand()' below moves the two # canceling "rscales" closer to each other in the hope of helping # with that. result = [(taker.diff(mi) * tgt_rscale**sum(mi)).expand() for mi in self.get_coefficient_identifiers()] logger.info("building translation operator: done") return result
def diff(diff_op, mi): eqs = [] for eq in diff_op.eqs: res = {} for deriv_ident, v in eq.items(): new_mi = add_mi(deriv_ident.mi, mi) res[DerivativeIdentifier(new_mi, deriv_ident.vec_idx)] = v eqs.append(pmap(res)) return LinearPDESystemOperator(diff_op.dim, *eqs)
def diff(pde, mi): res = {} for mi_to_coeff_mi, v in pde.mi_to_coeff.items(): res[add_mi(mi_to_coeff_mi, mi)] = v return DifferentialOperator(pde.dim, pmap(res))
def get_stored_ids_and_unscaled_projection_matrix(self): from pytools import ProcessLogger plog = ProcessLogger(logger, "compute PDE for Taylor coefficients") mis = self.get_full_coefficient_identifiers() coeff_ident_enumerate_dict = { tuple(mi): i for (i, mi) in enumerate(mis) } diff_op = self.get_pde_as_diff_op() assert len(diff_op.eqs) == 1 pde_dict = {k.mi: v for k, v in diff_op.eqs[0].items()} for ident in pde_dict.keys(): if ident not in coeff_ident_enumerate_dict: # Order of the expansion is less than the order of the PDE. # In that case, the compression matrix is the identity matrix # and there's nothing to project from_input_coeffs_by_row = [[(i, 1)] for i in range(len(mis))] from_output_coeffs_by_row = [[] for _ in range(len(mis))] shape = (len(mis), len(mis)) op = CSEMatVecOperator(from_input_coeffs_by_row, from_output_coeffs_by_row, shape) return mis, op # Calculate the multi-index that appears last in in the PDE in # reverse degree lexicographic order (degrevlex). max_mi_idx = max(coeff_ident_enumerate_dict[ident] for ident in pde_dict.keys()) max_mi = mis[max_mi_idx] max_mi_coeff = pde_dict[max_mi] max_mi_mult = -1 / sym.sympify(max_mi_coeff) def is_stored(mi): """ A multi_index mi is not stored if mi >= max_mi """ return any(mi[d] < max_mi[d] for d in range(self.dim)) stored_identifiers = [] from_input_coeffs_by_row = [] from_output_coeffs_by_row = [] for i, mi in enumerate(mis): # If the multi-index is to be stored, keep the projection matrix # entry empty if is_stored(mi): idx = len(stored_identifiers) stored_identifiers.append(mi) from_input_coeffs_by_row.append([(idx, 1)]) from_output_coeffs_by_row.append([]) continue diff = [mi[d] - max_mi[d] for d in range(self.dim)] # eg: u_xx + u_yy + u_zz is represented as # [((2, 0, 0), 1), ((0, 2, 0), 1), ((0, 0, 2), 1)] assignment = [] for other_mi, coeff in pde_dict.items(): j = coeff_ident_enumerate_dict[add_mi(other_mi, diff)] if i == j: # Skip the u_zz part here. continue # PDE might not have max_mi_coeff = -1, divide by -max_mi_coeff # to get a relation of the form, u_zz = - u_xx - u_yy for Laplace 3D. assignment.append((j, coeff * max_mi_mult)) from_input_coeffs_by_row.append([]) from_output_coeffs_by_row.append(assignment) plog.done() logger.debug( "number of Taylor coefficients was reduced from {orig} to {red}". format(orig=len(self.get_full_coefficient_identifiers()), red=len(stored_identifiers))) shape = (len(mis), len(stored_identifiers)) op = CSEMatVecOperator(from_input_coeffs_by_row, from_output_coeffs_by_row, shape) return stored_identifiers, op
def translate_from(self, src_expansion, src_coeff_exprs, src_rscale, dvec, tgt_rscale): logger.info("building translation operator: %s(%d) -> %s(%d): start" % (type(src_expansion).__name__, src_expansion.order, type(self).__name__, self.order)) if not self.use_rscale: src_rscale = 1 tgt_rscale = 1 from sumpy.expansion.multipole import VolumeTaylorMultipoleExpansionBase if isinstance(src_expansion, VolumeTaylorMultipoleExpansionBase): # We know the general form of the multipole expansion is: # # coeff0 * diff(kernel, mi0) + coeff1 * diff(kernel, mi1) + ... # # To get the local expansion coefficients, we take derivatives of # the multipole expansion. # # This code speeds up derivative taking by caching all kernel # derivatives. taker = src_expansion.get_kernel_derivative_taker(dvec) from sumpy.tools import add_mi result = [] for deriv in self.get_coefficient_identifiers(): local_result = [] for coeff, term in zip( src_coeff_exprs, src_expansion.get_coefficient_identifiers()): kernel_deriv = ( src_expansion.get_scaled_multipole( taker.diff(add_mi(deriv, term)), dvec, src_rscale, nderivatives=sum(deriv) + sum(term), nderivatives_for_scaling=sum(term))) local_result.append( coeff * kernel_deriv * tgt_rscale**sum(deriv)) result.append(sym.Add(*local_result)) else: from sumpy.tools import MiDerivativeTaker expr = src_expansion.evaluate(src_coeff_exprs, dvec, rscale=src_rscale) taker = MiDerivativeTaker(expr, dvec) # Rscale/operand magnitude is fairly sensitive to the order of # operations--which is something we don't have fantastic control # over at the symbolic level. The '.expand()' below moves the two # canceling "rscales" closer to each other in the hope of helping # with that. result = [ (taker.diff(mi) * tgt_rscale**sum(mi)).expand() for mi in self.get_coefficient_identifiers()] logger.info("building translation operator: done") return result
def translate_from(self, src_expansion, src_coeff_exprs, src_rscale, dvec, tgt_rscale, sac=None): logger.info("building translation operator: %s(%d) -> %s(%d): start" % (type(src_expansion).__name__, src_expansion.order, type(self).__name__, self.order)) if not self.use_rscale: src_rscale = 1 tgt_rscale = 1 from sumpy.expansion.multipole import VolumeTaylorMultipoleExpansionBase if isinstance(src_expansion, VolumeTaylorMultipoleExpansionBase): # We know the general form of the multipole expansion is: # # coeff0 * diff(kernel, mi0) + coeff1 * diff(kernel, mi1) + ... # # To get the local expansion coefficients, we take derivatives of # the multipole expansion. # # This code speeds up derivative taking by caching all kernel # derivatives. taker = src_expansion.get_kernel_derivative_taker(dvec) from sumpy.tools import add_mi # Calculate a elementwise maximum multi-index because the number # of multi-indices needed is much less than # gnitstam(src_order + tgt order) when PDE conforming expansions # are used. For full Taylor, there's no difference. def calc_max_mi(mis): return (max(mi[i] for mi in mis) for i in range(self.dim)) src_max_mi = calc_max_mi( src_expansion.get_coefficient_identifiers()) tgt_max_mi = calc_max_mi(self.get_coefficient_identifiers()) max_mi = add_mi(src_max_mi, tgt_max_mi) # Create a expansion terms wrangler for derivatives up to order # (tgt order)+(src order) including a corresponding reduction matrix tgtplusderiv_exp_terms_wrangler = \ src_expansion.expansion_terms_wrangler.copy( order=self.order + src_expansion.order, max_mi=tuple(max_mi)) tgtplusderiv_coeff_ids = \ tgtplusderiv_exp_terms_wrangler.get_coefficient_identifiers() tgtplusderiv_full_coeff_ids = \ tgtplusderiv_exp_terms_wrangler.get_full_coefficient_identifiers() tgtplusderiv_ident_to_index = { ident: i for i, ident in enumerate(tgtplusderiv_full_coeff_ids) } result = [] for lexp_mi in self.get_coefficient_identifiers(): lexp_mi_terms = [] # Embed the source coefficients in the coefficient array # for the (tgt order)+(src order) wrangler, offset by lexp_mi. embedded_coeffs = [0] * len(tgtplusderiv_full_coeff_ids) for coeff, term in zip( src_coeff_exprs, src_expansion.get_coefficient_identifiers()): embedded_coeffs[ tgtplusderiv_ident_to_index[add_mi(lexp_mi, term)]] \ = coeff # Compress the embedded coefficient set stored_coeffs = tgtplusderiv_exp_terms_wrangler \ .get_stored_mpole_coefficients_from_full( embedded_coeffs, src_rscale, sac=sac) # Sum the embedded coefficient set for tgtplusderiv_coeff_id, coeff in zip( tgtplusderiv_coeff_ids, stored_coeffs): if coeff == 0: continue nderivatives_for_scaling = \ sum(tgtplusderiv_coeff_id)-sum(lexp_mi) kernel_deriv = (src_expansion.get_scaled_multipole( taker.diff(tgtplusderiv_coeff_id), dvec, src_rscale, nderivatives=sum(tgtplusderiv_coeff_id), nderivatives_for_scaling=nderivatives_for_scaling)) lexp_mi_terms.append(coeff * kernel_deriv * tgt_rscale**sum(lexp_mi)) result.append(sym.Add(*lexp_mi_terms)) else: from sumpy.tools import MiDerivativeTaker # Rscale/operand magnitude is fairly sensitive to the order of # operations--which is something we don't have fantastic control # over at the symbolic level. Scaling dvec, then differentiating, # and finally rescaling dvec leaves the expression needing a scaling # to compensate for differentiating which is done at the end. # This moves the two cancelling "rscales" closer to each other at # the end in the hope of helping rscale magnitude. dvec_scaled = [d * src_rscale for d in dvec] expr = src_expansion.evaluate(src_coeff_exprs, dvec_scaled, rscale=src_rscale, sac=sac) replace_dict = dict((d, d / src_rscale) for d in dvec) taker = MiDerivativeTaker(expr, dvec) rscale_ratio = sym.UnevaluatedExpr(tgt_rscale / src_rscale) result = [ (taker.diff(mi).xreplace(replace_dict) * rscale_ratio**sum(mi)) for mi in self.get_coefficient_identifiers() ] logger.info("building translation operator: done") return result
def as_scalar_pde(pde, vec_idx): r""" Returns a scalar PDE that is satisfied by the *vec_idx* component of *pde*. :arg pde: An instance of :class:`LinearPDESystemOperator` :arg vec_idx: the index of the vector-valued function that we want as a scalar PDE """ from sumpy.tools import nullspace indices = set() for eq in pde.eqs: for deriv_ident in eq.keys(): indices.add(deriv_ident.vec_idx) # this is already a scalar pde if len(indices) == 1 and list(indices)[0] == vec_idx: return pde from pytools import ProcessLogger plog = ProcessLogger(logger, "computing single PDE for multiple PDEs") from pytools import ( generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam) dim = pde.total_dims # slowly increase the order of the derivatives that we take of the # system of PDEs. Once we reach the order of the scalar PDE, this # loop will break for order in range(2, 100): mis = sorted(gnitstam(order, dim), key=sum) pde_mat = [] coeff_ident_enumerate_dict = dict((tuple(mi), i) for (i, mi) in enumerate(mis)) offset = len(mis) # Create a matrix of equations that are derivatives of the # original system of PDEs for mi in mis: for pde_dict in pde.eqs: eq = [0]*(len(mis)*(max(indices)+1)) for ident, coeff in pde_dict.items(): c = tuple(add_mi(ident.mi, mi)) if c not in coeff_ident_enumerate_dict: break idx = offset*ident.vec_idx + coeff_ident_enumerate_dict[c] eq[idx] = coeff else: pde_mat.append(eq) if len(pde_mat) == 0: continue # Get the nullspace of the matrix and get the rows related to this # vec_idx n = nullspace(pde_mat)[offset*vec_idx:offset*(vec_idx+1), :] indep_row = find_linear_relationship(n) if len(indep_row) > 0: pde_dict = {} mult = indep_row[max(indep_row.keys())] for k, v in indep_row.items(): pde_dict[DerivativeIdentifier(mis[k], 0)] = v / mult plog.done() return LinearPDESystemOperator(pde.dim, pmap(pde_dict)) plog.done() assert False