Exemple #1
0
    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
Exemple #2
0
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)
Exemple #3
0
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))
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
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