def dicts_lt(a, b):
    na = 0 if a is None else len(a)
    nb = 0 if b is None else len(b)
    if na != nb:
        return len(a) < len(b)
    for ia, ib in zip(sorted_by_key(a), sorted_by_key(b)):
        # Assuming keys are sortable (usually str)
        if ia[0] != ib[0]:
            return (ia[0].__class__.__name__, ia[0]) < (ib[0].__class__.__name__, ib[0])  # Hack to preserve type sorting in py3
        # Assuming values are sortable
        if ia[1] != ib[1]:
            return (ia[1].__class__.__name__, ia[1]) < (ib[1].__class__.__name__, ib[1])  # Hack to preserve type sorting in py3
Beispiel #2
0
def _extract_entity_dofs(element, indices):
    # FIXME: Readability counts
    entity_dofs = element.entity_dofs()
    dofs = {}
    for (dim, entities) in sorted_by_key(entity_dofs):
        dofs[dim] = {}
        for (entity, all_dofs) in sorted_by_key(entities):
            dofs[dim][entity] = []
            for index in all_dofs:
                if index in indices:
                    # print "index = ", index
                    i = indices.index(index)
                    dofs[dim][entity] += [i]
    return dofs
Beispiel #3
0
def _extract_entity_dofs(element, indices):
    # FIXME: Readability counts
    entity_dofs = element.entity_dofs()
    dofs = {}
    for (dim, entities) in sorted_by_key(entity_dofs):
        dofs[dim] = {}
        for (entity, all_dofs) in sorted_by_key(entities):
            dofs[dim][entity] = []
            for index in all_dofs:
                if index in indices:
                    # print "index = ", index
                    i = indices.index(index)
                    dofs[dim][entity] += [i]
    return dofs
Beispiel #4
0
    def get_var_occurrences(self):
        """Determine the number of minimum number of times all variables
        occurs in the expression. Returns a dictionary of variables
        and the number of times they occur. x*x + x returns {x:1}, x +
        y returns {}.

        """
        # NOTE: This function is only used if the numerator of a
        # Fraction is a Sum.

        # Get occurrences in first expression.
        d0 = self.vrs[0].get_var_occurrences()
        for var in self.vrs[1:]:
            # Get the occurrences.
            d = var.get_var_occurrences()
            # Delete those variables in d0 that are not in d.
            for k, v in list(d0.items()):
                if k not in d:
                    del d0[k]
            # Set the number of occurrences equal to the smallest
            # number.
            for k, v in sorted_by_key(d):
                if k in d0:
                    d0[k] = min(d0[k], v)
        return d0
Beispiel #5
0
    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a list of tuples
        [(found, remain)], where 'found' is an expression that only
        has variables of type == var_type. If no variables are found,
        found=(). The 'remain' part contains the leftover after
        division by 'found' such that: self = Sum([f*r for f,r in
        self.reduce_vartype(Type)]).

        """
        found = {}
        # Loop members and reduce them by vartype.
        for v in self.vrs:
            for f, r in v.reduce_vartype(var_type):
                if f in found:
                    found[f].append(r)
                else:
                    found[f] = [r]

        # Create the return value.
        returns = []
        for f, r in sorted_by_key(found):
            if len(r) > 1:
                # Use expand to group expressions.
                r = create_sum(r)
            elif r:
                r = r.pop()
            returns.append((f, r))
        return sorted(returns)
Beispiel #6
0
def _indices(element, restriction_domain, tdim):
    "Extract basis functions indices that correspond to restriction_domain."

    # FIXME: The restriction_domain argument in FFC/UFL needs to be re-thought and
    # cleaned-up.

    # If restriction_domain is "interior", pick basis functions associated with
    # cell.
    if restriction_domain == "interior":
        return element.entity_dofs()[tdim][0]

    # Pick basis functions associated with
    # the topological degree of the restriction_domain and of all lower
    # dimensions.
    if restriction_domain == "facet":
        rdim = tdim-1
    elif restriction_domain == "face":
        rdim = 2
    elif restriction_domain == "edge":
        rdim = 1
    elif restriction_domain == "vertex":
        rdim = 0
    else:
        error("Restriction to domain: %s, is not supported." % repr(restriction_domain))

    entity_dofs = element.entity_dofs()
    indices = []
    for dim in range(rdim + 1):
        entities = entity_dofs[dim]
        for (entity, index) in sorted_by_key(entities):
            indices += index
    return indices
Beispiel #7
0
def _overlap(l, d):
    "Check if a member in list l is in the value (list) of dictionary d."
    for m in l:
        for k, v in sorted_by_key(d):
            if m in v:
                return True
    return False
    def sum(self, o, *operands):
        code = {}
        # Loop operands that has to be summend.
        for op in operands:
            # If entries does already exist we can add the code,
            # otherwise just dump them in the element tensor.
            for key, val in sorted(op.items()):
                if key in code:
                    code[key].append(val)
                else:
                    code[key] = [val]

        # Add sums and group if necessary.
        for key, val in sorted_by_key(code):
            if len(val) > 1:
                code[key] = create_sum(val)
            elif val:
                code[key] = val[0]
            else:
                error("Where did the values go?")
            # If value is zero just ignore it.
            if abs(code[key].val) < format["epsilon"]:
                del code[key]

        return code
Beispiel #9
0
    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a list of tuples
        [(found, remain)], where 'found' is an expression that only
        has variables of type == var_type. If no variables are found,
        found=(). The 'remain' part contains the leftover after
        division by 'found' such that: self = Sum([f*r for f,r in
        self.reduce_vartype(Type)]).

        """
        found = {}
        # Loop members and reduce them by vartype.
        for v in self.vrs:
            for f, r in v.reduce_vartype(var_type):
                if f in found:
                    found[f].append(r)
                else:
                    found[f] = [r]

        # Create the return value.
        returns = []
        for f, r in sorted_by_key(found):
            if len(r) > 1:
                # Use expand to group expressions.
                r = create_sum(r)
            elif r:
                r = r.pop()
            returns.append((f, r))
        return sorted(returns)
Beispiel #10
0
def dicts_lt(a, b):
    na = 0 if a is None else len(a)
    nb = 0 if b is None else len(b)
    if na != nb:
        return len(a) < len(b)
    for ia, ib in zip(sorted_by_key(a), sorted_by_key(b)):
        # Assuming keys are sortable (usually str)
        if ia[0] != ib[0]:
            return (ia[0].__class__.__name__, ia[0]) < (
                ib[0].__class__.__name__, ib[0]
            )  # Hack to preserve type sorting in py3
        # Assuming values are sortable
        if ia[1] != ib[1]:
            return (ia[1].__class__.__name__, ia[1]) < (
                ib[1].__class__.__name__, ib[1]
            )  # Hack to preserve type sorting in py3
def _simplify_expression(integral, geo_consts, psi_tables_map):
    for points, terms, functions, ip_consts, coordinate, conditionals in integral:
        # NOTE: sorted is needed to pass the regression tests on the buildbots
        # but it might be inefficient for speed.
        # A solution could be to only compare the output of evaluating the
        # integral, not the header files.
        for loop, (data, entry_vals) in sorted_by_key(terms):
            t_set, u_weights, u_psi_tables, u_nzcs, basis_consts = data
            new_entry_vals = []
            psi_tables = set()
            # NOTE: sorted is needed to pass the regression tests on the buildbots
            # but it might be inefficient for speed.
            # A solution could be to only compare the output of evaluating the
            # integral, not the header files.
            for entry, val, ops in sorted(entry_vals):
                value = optimise_code(val, ip_consts, geo_consts, t_set)
                # Check if value is zero
                if value.val:
                    new_entry_vals.append((entry, value, value.ops()))
                    psi_tables.update(
                        set([
                            psi_tables_map[b]
                            for b in value.get_unique_vars(BASIS)
                        ]))

            terms[loop][0][2] = psi_tables
            terms[loop][1] = new_entry_vals
    def sum(self, o, *operands):
        code = {}
        # Loop operands that has to be summend.
        for op in operands:
            # If entries does already exist we can add the code,
            # otherwise just dump them in the element tensor.
            for key, val in sorted(op.items()):
                if key in code:
                    code[key].append(val)
                else:
                    code[key] = [val]

        # Add sums and group if necessary.
        for key, val in sorted_by_key(code):
            if len(val) > 1:
                code[key] = create_sum(val)
            elif val:
                code[key] = val[0]
            else:
                error("Where did the values go?")
            # If value is zero just ignore it.
            if abs(code[key].val) < format["epsilon"]:
                del code[key]

        return code
Beispiel #13
0
def generate_aux_constants(constant_decl, name, var_type, print_ops=False):
    "A helper tool to generate code for constant declarations."
    format_comment = format["comment"]
    code = []
    append = code.append
    ops = 0
    for num, expr in sorted((v, k) for k, v in sorted_by_key(constant_decl)):
#        debug("expr orig: " + str(expr))
#        print "\nnum: ", num
#        print "expr orig: " + repr(expr)
#        print "expr exp: " + str(expr.expand())
        # Expand and reduce expression (If we don't already get reduced expressions.)
        expr = expr.expand().reduce_ops()
#        debug("expr opt:  " + str(expr))
#        print "expr opt:  " + str(expr)
        if print_ops:
            op = expr.ops()
            ops += op
            append(format_comment("Number of operations: %d" %op))
            append(var_type(name(num), str(expr)))
            append("")
        else:
            ops += expr.ops()
            append(var_type(name(num), str(expr)))

    return (ops, code)
Beispiel #14
0
    def get_var_occurrences(self):
        """Determine the number of minimum number of times all variables
        occurs in the expression. Returns a dictionary of variables
        and the number of times they occur. x*x + x returns {x:1}, x +
        y returns {}.

        """
        # NOTE: This function is only used if the numerator of a
        # Fraction is a Sum.

        # Get occurrences in first expression.
        d0 = self.vrs[0].get_var_occurrences()
        for var in self.vrs[1:]:
            # Get the occurrences.
            d = var.get_var_occurrences()
            # Delete those variables in d0 that are not in d.
            for k, v in list(d0.items()):
                if k not in d:
                    del d0[k]
            # Set the number of occurrences equal to the smallest
            # number.
            for k, v in sorted_by_key(d):
                if k in d0:
                    d0[k] = min(d0[k], v)
        return d0
Beispiel #15
0
def _overlap(l, d):
    "Check if a member in list l is in the value (list) of dictionary d."
    for m in l:
        for k, v in sorted_by_key(d):
            if m in v:
                return True
    return False
Beispiel #16
0
def _indices(element, restriction_domain, tdim):
    "Extract basis functions indices that correspond to restriction_domain."

    # FIXME: The restriction_domain argument in FFC/UFL needs to be re-thought and
    # cleaned-up.

    # If restriction_domain is "interior", pick basis functions associated with
    # cell.
    if restriction_domain == "interior":
        return element.entity_dofs()[tdim][0]

    # Pick basis functions associated with
    # the topological degree of the restriction_domain and of all lower
    # dimensions.
    if restriction_domain == "facet":
        rdim = tdim - 1
    elif restriction_domain == "face":
        rdim = 2
    elif restriction_domain == "edge":
        rdim = 1
    elif restriction_domain == "vertex":
        rdim = 0
    else:
        error("Restriction to domain: %s, is not supported." %
              repr(restriction_domain))

    entity_dofs = element.entity_dofs()
    indices = []
    for dim in range(rdim + 1):
        entities = entity_dofs[dim]
        for (entity, index) in sorted_by_key(entities):
            indices += index
    return indices
Beispiel #17
0
def compile_with_error_control(forms, object_names, reserved_objects, prefix,
                               parameters):
    """
    Compile forms and additionally generate and compile forms required
    for performing goal-oriented error control

    For linear problems, the input forms should be a bilinear form (a)
    and a linear form (L) specifying the variational problem and
    additionally a linear form (M) specifying the goal functional.

    For nonlinear problems, the input should be linear form (F) and a
    functional (M) specifying the goal functional.

    *Arguments*

        forms (tuple)
            Three (linear case) or two (nonlinear case) forms
            specifying the primal problem and the goal

        object_names (dict)
            Map from object ids to object names

        reserved_names (dict)
            Map from reserved object names to object ids

        prefix (string)
            Basename of header file

        parameters (dict)
            Parameters for form compilation
    """

    # Check input arguments
    F, M, u = prepare_input_arguments(forms, object_names, reserved_objects)

    # Generate forms to be used for the error control
    from ffc.errorcontrol.errorcontrolgenerators import UFLErrorControlGenerator
    generator = UFLErrorControlGenerator(F, M, u)
    ec_forms = generator.generate_all_error_control_forms()

    # Check that there are no conflicts between user defined and
    # generated names
    ec_names = generator.ec_names
    if set(object_names.values()) & set(ec_names.values()):
        comment = "%s are reserved error control names." % str(
            sorted(ec_names.values()))
        error("Conflict between user defined and generated names: %s" %
              comment)

    # Add names generated for error control to object_names
    for (objid, name) in sorted_by_key(ec_names):
        object_names[objid] = name

    # Compile error control and input (pde + goal) forms as normal
    forms = generator.primal_forms()
    code_h, code_c = compile_form(ec_forms + forms, object_names, prefix,
                                  parameters)

    return code_h, code_c
Beispiel #18
0
def compile_with_error_control(forms, object_names, reserved_objects,
                               prefix, parameters):
    """
    Compile forms and additionally generate and compile forms required
    for performing goal-oriented error control

    For linear problems, the input forms should be a bilinear form (a)
    and a linear form (L) specifying the variational problem and
    additionally a linear form (M) specifying the goal functional.

    For nonlinear problems, the input should be linear form (F) and a
    functional (M) specifying the goal functional.

    *Arguments*

        forms (tuple)
            Three (linear case) or two (nonlinear case) forms
            specifying the primal problem and the goal

        object_names (dict)
            Map from object ids to object names

        reserved_names (dict)
            Map from reserved object names to object ids

        prefix (string)
            Basename of header file

        parameters (dict)
            Parameters for form compilation
    """

    # Check input arguments
    F, M, u = prepare_input_arguments(forms, object_names, reserved_objects)

    # Generate forms to be used for the error control
    from ffc.errorcontrol.errorcontrolgenerators import UFLErrorControlGenerator
    generator = UFLErrorControlGenerator(F, M, u)
    ec_forms = generator.generate_all_error_control_forms()

    # Check that there are no conflicts between user defined and
    # generated names
    ec_names = generator.ec_names
    if set(object_names.values()) & set(ec_names.values()):
        comment = "%s are reserved error control names." % str(sorted(ec_names.values()))
        error("Conflict between user defined and generated names: %s" % comment)

    # Add names generated for error control to object_names
    for (objid, name) in sorted_by_key(ec_names):
        object_names[objid] = name

    # Compile error control and input (pde + goal) forms as normal
    forms = generator.primal_forms()
    code_h, code_c = compile_form(ec_forms + forms, object_names, prefix, parameters)

    return code_h, code_c
Beispiel #19
0
def _precompute_expressions(integral, geo_consts, optimisation):
    for points, terms, functions, ip_consts, coordinate, conditionals in integral:
        for loop, (data, entry_vals) in sorted_by_key(terms):
            t_set, u_weights, u_psi_tables, u_nzcs, basis_consts = data
            new_entry_vals = []
            for entry, val, ops in entry_vals:
                value = _extract_variables(val, basis_consts, ip_consts, geo_consts, t_set, optimisation)
                # Check if value is zero
                if value.val:
                    new_entry_vals.append((entry, value, value.ops()))
            terms[loop][1] = new_entry_vals
def _precompute_expressions(integral, geo_consts, optimisation):
    for points, terms, functions, ip_consts, coordinate, conditionals in integral:
        for loop, (data, entry_vals) in sorted_by_key(terms):
            t_set, u_weights, u_psi_tables, u_nzcs, basis_consts = data
            new_entry_vals = []
            for entry, val, ops in entry_vals:
                value = _extract_variables(val, basis_consts, ip_consts,
                                           geo_consts, t_set, optimisation)
                # Check if value is zero
                if value.val:
                    new_entry_vals.append((entry, value, value.ops()))
            terms[loop][1] = new_entry_vals
Beispiel #21
0
def reduction_possible(variables):
    """Find the variable that occurs in the most products, if more variables
    occur the same number of times and in the same products add them to list."""

    # Find the variable that appears in the most products
    max_val = 1
    max_var = ""
    max_vars = []
    for key, val in sorted_by_key(variables):
        if max_val < val[0]:
            max_val = val[0]
            max_var = key

    # If we found a variable that appears in products multiple times, check if
    # other variables appear in the exact same products
    if max_var:
        for key, val in sorted_by_key(variables):
            # Check if we have more variables in the same products
            if max_val == val[0] and variables[max_var][1] == val[1]:
                max_vars.append(key)
    return max_vars
Beispiel #22
0
def debug_dict(d, title=""):
    "Pretty-print dictionary."
    if not title: title = "Dictionary"
    info("")
    begin(title)
    info("")
    for (key, value) in sorted_by_key(d):
        info(key)
        info("-" * len(key))
        info(str(value))
        info("")
    end()
Beispiel #23
0
def reduction_possible(variables):
    """Find the variable that occurs in the most products, if more variables
    occur the same number of times and in the same products add them to list."""

    # Find the variable that appears in the most products
    max_val = 1
    max_var = ""
    max_vars = []
    for key, val in sorted_by_key(variables):
        if max_val < val[0]:
            max_val = val[0]
            max_var = key

    # If we found a variable that appears in products multiple times, check if
    # other variables appear in the exact same products
    if max_var:
        for key, val in sorted_by_key(variables):
            # Check if we have more variables in the same products
            if max_val == val[0] and variables[max_var][1] == val[1]:
                max_vars.append(key)
    return max_vars
Beispiel #24
0
Datei: log.py Projekt: FEniCS/ffc
def debug_dict(d, title=""):
    "Pretty-print dictionary."
    if not title:
        title = "Dictionary"
    info("")
    begin(title)
    info("")
    for (key, value) in sorted_by_key(d):
        info(key)
        info("-" * len(key))
        info(str(value))
        info("")
    end()
Beispiel #25
0
def group_form_integrals(form, domains):
    """Group integrals by domain and type, performing canonical simplification.

    :arg form: the :class:`~.Form` to group the integrals of.
    :arg domains: an iterable of :class:`~.Domain`\s.
    :returns: A new :class:`~.Form` with gathered integrands.
    """
    # Group integrals by domain and type
    integrals_by_domain_and_type = \
        group_integrals_by_domain_and_type(form.integrals(), domains)

    integrals = []
    for domain in domains:
        for integral_type in ufl.measure.integral_types():
            # Get integrals with this domain and type
            ddt_integrals = integrals_by_domain_and_type.get(
                (domain, integral_type))
            if ddt_integrals is None:
                continue

            # Group integrals by subdomain id, after splitting e.g.
            #   f*dx((1,2)) + g*dx((2,3)) -> f*dx(1) + (f+g)*dx(2) + g*dx(3)
            # (note: before this call, 'everywhere' is a valid subdomain_id,
            # and after this call, 'otherwise' is a valid subdomain_id)
            single_subdomain_integrals = \
                rearrange_integrals_by_single_subdomains(ddt_integrals)

            for subdomain_id, ss_integrals in sorted_by_key(
                    single_subdomain_integrals):
                # Accumulate integrands of integrals that share the
                # same compiler data
                integrands_and_cds = \
                    accumulate_integrands_with_same_metadata(ss_integrals)

                for integrand, metadata in integrands_and_cds:
                    integrals.append(
                        Integral(integrand, integral_type, domain,
                                 subdomain_id, metadata, None))
    return Form(integrals)
Beispiel #26
0
def _simplify_expression(integral, geo_consts, psi_tables_map):
    for points, terms, functions, ip_consts, coordinate, conditionals in integral:
        # NOTE: sorted is needed to pass the regression tests on the buildbots
        # but it might be inefficient for speed.
        # A solution could be to only compare the output of evaluating the
        # integral, not the header files.
        for loop, (data, entry_vals) in sorted_by_key(terms):
            t_set, u_weights, u_psi_tables, u_nzcs, basis_consts = data
            new_entry_vals = []
            psi_tables = set()
            # NOTE: sorted is needed to pass the regression tests on the buildbots
            # but it might be inefficient for speed.
            # A solution could be to only compare the output of evaluating the
            # integral, not the header files.
            for entry, val, ops in sorted(entry_vals):
                value = optimise_code(val, ip_consts, geo_consts, t_set)
                # Check if value is zero
                if value.val:
                    new_entry_vals.append((entry, value, value.ops()))
                    psi_tables.update(set([psi_tables_map[b] for b in value.get_unique_vars(BASIS)]))

            terms[loop][0][2] = psi_tables
            terms[loop][1] = new_entry_vals
Beispiel #27
0
def _indices(element, restriction_domain, dim=0):
    "Extract basis functions indices that correspond to restriction_domain."

    # FIXME: The restriction_domain argument in FFC/UFL needs to be re-thought and
    # cleaned-up.

    # If restriction_domain is "interior", pick basis functions associated with
    # cell.
    if restriction_domain == "interior" and dim:
        return element.entity_dofs()[dim][0]

    # If restriction_domain is a ufl.Cell, pick basis functions associated with
    # the topological degree of the restriction_domain and of all lower
    # dimensions.
    if isinstance(restriction_domain, ufl.Cell):
        dim = restriction_domain.topological_dimension()
        entity_dofs = element.entity_dofs()
        indices = []
        for dim in range(restriction_domain.topological_dimension() + 1):
            entities = entity_dofs[dim]
            for (entity, index) in sorted_by_key(entities):
                indices += index
        return indices

    # Just extract all indices to make handling in RestrictedElement
    # uniform.
    #elif isinstance(restriction_domain, ufl.Measure):
    #    indices = []
    #    entity_dofs = element.entity_dofs()
    #    for dim, entities in entity_dofs.items():
    #        for entity, index in entities.items():
    #            indices += index
    #    return indices

    else:
        error("Restriction to domain: %s, is not supported." %
              repr(restriction_domain))
Beispiel #28
0
 def tabulate(self, order, points):
     result = self._element.tabulate(order, points)
     extracted = {}
     for (dtuple, values) in sorted_by_key(result):
         extracted[dtuple] = numpy.array([values[i] for i in self._indices])
     return extracted
Beispiel #29
0
    def reduce_ops(self):
        "Reduce the number of operations needed to evaluate the sum."
#        global ind
#        ind += " "
#        print "\n%sreduce_ops, start" % ind

        if self._reduced:
            return self._reduced
        # NOTE: Assuming that sum has already been expanded.
        # TODO: Add test for this and handle case if it is not.

        # TODO: The entire function looks expensive, can it be optimised?

        # TODO: It is not necessary to create a new Sum if we do not have more
        # than one Fraction.
        # First group all fractions in the sum.
        new_sum = _group_fractions(self)
        if new_sum._prec != 3: # sum
            self._reduced = new_sum.reduce_ops()
            return self._reduced
        # Loop all variables of the sum and collect the number of common
        # variables that can be factored out.
        common_vars = {}
        for var in new_sum.vrs:
            # Get dictonary of occurrences and add the variable and the number
            # of occurrences to common dictionary.
            for k, v in sorted_by_key(var.get_var_occurrences()):
#                print
#                print ind + "var: ", var
#                print ind + "k: ", k
#                print ind + "v: ", v
                if k in common_vars:
                    common_vars[k].append((v, var))
                else:
                    common_vars[k] = [(v, var)]
#        print
#        print "common vars: "
#        for k,v in common_vars.items():
#            print "k: ", k
#            print "v: ", v
#        print
        # Determine the maximum reduction for each variable
        # sorted as: {(x*x*y, x*y*z, 2*y):[2, [y]]}.
        terms_reductions = {}
        for k, v in sorted_by_key(common_vars):
#            print
#            print ind + "k: ", k
#            print ind + "v: ", v
            # If the number of expressions that can be reduced is only one
            # there is nothing to be done.
            if len(v) > 1:
                # TODO: Is there a better way to compute the reduction gain
                # and the number of occurrences we should remove?

                # Get the list of number of occurences of 'k' in expressions
                # in 'v'.
                occurrences = [t[0] for t in v]

                # Determine the favorable number of occurences and an estimate
                # of the maximum reduction for current variable.
                fav_occur = 0
                reduc = 0
                for i in set(occurrences):
                    # Get number of terms that has a number of occcurences equal
                    # to or higher than the current number.
                    num_terms = len([o for o in occurrences if o >= i])

                    # An estimate of the reduction in operations is:
                    # (number_of_terms - 1) * number_occurrences.
                    new_reduc = (num_terms-1)*i
                    if new_reduc > reduc:
                        reduc = new_reduc
                        fav_occur = i

                # Extract the terms of v where the number of occurrences is
                # equal to or higher than the most favorable number of occurrences.
                terms = sorted([t[1] for t in v if t[0] >= fav_occur])

                # We need to reduce the expression with the favorable number of
                # occurrences of the current variable.
                red_vars = [k]*fav_occur

                # If the list of terms is already present in the dictionary,
                # add the reduction count and the variables.
                if tuple(terms) in terms_reductions:
                    terms_reductions[tuple(terms)][0] += reduc
                    terms_reductions[tuple(terms)][1] += red_vars
                else:
                    terms_reductions[tuple(terms)] = [reduc, red_vars]
#        print "\nterms_reductions: "
#        for k,v in terms_reductions.items():
#            print "k: ", create_sum(k)
#            print "v: ", v
#        print "red: self: ", self
        if terms_reductions:
            # Invert dictionary of terms.
            reductions_terms = dict([((v[0], tuple(v[1])), k) for k, v in six.iteritems(terms_reductions)])

            # Create a sorted list of those variables that give the highest
            # reduction.
            sorted_reduc_var = sorted(six.iterkeys(reductions_terms), reverse=True)
#            sorted_reduc_var = [k for k, v in six.iteritems(reductions_terms)]
#            print
#            print ind + "raw"
#            for k in sorted_reduc_var:
#                print ind, k[0], k[1]
#            sorted_reduc_var.sort()
#            sorted_reduc_var.sort(lambda x, y: cmp(x[0], y[0]))
#            sorted_reduc_var.reverse()
#            print ind + "sorted"
#            for k in sorted_reduc_var:
#                print ind, k[0], k[1]

            # Create a new dictionary of terms that should be reduced, if some
            # terms overlap, only pick the one which give the highest reduction to
            # ensure that a*x*x + b*x*x + x*x*y + 2*y -> x*x*(a + b + y) + 2*y NOT
            # x*x*(a + b) + y*(2 + x*x).
            reduction_vars = {}
            rejections = {}
            for var in sorted_reduc_var:
                terms = reductions_terms[var]
                if _overlap(terms, reduction_vars) or _overlap(terms, rejections):
                    rejections[var[1]] = terms
                else:
                    reduction_vars[var[1]] = terms

#            print "\nreduction_vars: "
#            for k,v in reduction_vars.items():
#                print "k: ", k
#                print "v: ", v

            # Reduce each set of terms with appropriate variables.
            all_reduced_terms = []
            reduced_expressions = []
            for reduc_var, terms in sorted(six.iteritems(reduction_vars)):

                # Add current terms to list of all variables that have been reduced.
                all_reduced_terms += list(terms)

                # Create variable that we will use to reduce the terms.
                reduction_var = None
                if len(reduc_var) > 1:
                    reduction_var = create_product(list(reduc_var))
                else:
                    reduction_var = reduc_var[0]

                # Reduce all terms that need to be reduced.
                reduced_terms = [t.reduce_var(reduction_var) for t in terms]

                # Create reduced expression.
                reduced_expr = None
                if len(reduced_terms) > 1:
                    # Try to reduce the reduced terms further.
                    reduced_expr = create_product([reduction_var, create_sum(reduced_terms).reduce_ops()])
                else:
                    reduced_expr = create_product(reduction_var, reduced_terms[0])

                # Add reduced expression to list of reduced expressions.
                reduced_expressions.append(reduced_expr)

            # Create list of terms that should not be reduced.
            dont_reduce_terms = []
            for v in new_sum.vrs:
                if not v in all_reduced_terms:
                    dont_reduce_terms.append(v)

            # Create expression from terms that was not reduced.
            not_reduced_expr = None
            if dont_reduce_terms and len(dont_reduce_terms) > 1:
                # Try to reduce the remaining terms that were not reduced at first.
                not_reduced_expr = create_sum(dont_reduce_terms).reduce_ops()
            elif dont_reduce_terms:
                not_reduced_expr = dont_reduce_terms[0]

            # Create return expression.
            if not_reduced_expr:
                self._reduced = create_sum(reduced_expressions + [not_reduced_expr])
            elif len(reduced_expressions) > 1:
                self._reduced = create_sum(reduced_expressions)
            else:
                self._reduced = reduced_expressions[0]
#            # NOTE: Only switch on for debugging.
#            if not self._reduced.expand() == self.expand():
#                print reduced_expressions[0]
#                print reduced_expressions[0].expand()
#                print "self: ", self
#                print "red:  ", repr(self._reduced)
#                print "self.exp: ", self.expand()
#                print "red.exp:  ", self._reduced.expand()
#                error("Reduced expression is not equal to original expression.")
            return self._reduced

        # Return self if we don't have any variables for which we can reduce
        # the sum.
        self._reduced = self
        return self._reduced
Beispiel #30
0
def _evaluate_basis_at_quadrature_points(psi_tables, gdim, element_data,
                                         form_prefix, num_vertices, num_cells):
    "Generate code for calling evaluate basis (derivatives) at quadrature points"

    # Prefetch formats to speed up code generation
    f_comment = format["comment"]
    f_declaration = format["declaration"]
    f_static_array = format["static array"]
    f_loop = format["generate loop"]
    f_eval_basis_decl = format["eval_basis_decl"]
    f_eval_basis_init = format["eval_basis_init"]
    f_eval_basis = format["eval_basis"]
    f_eval_basis_copy = format["eval_basis_copy"]
    f_eval_derivs_decl = format["eval_derivs_decl"]
    f_eval_derivs_init = format["eval_derivs_init"]
    f_eval_derivs = format["eval_derivs"]
    f_eval_derivs_copy = format["eval_derivs_copy"]

    code = []

    # Extract prefixes for tables
    prefixes = sorted(set(table.split("_")[0] for table in psi_tables))

    # Use lower case prefix for form name
    form_prefix = form_prefix.lower()

    # The psi_tables used by the quadrature code are for scalar
    # components of specific derivatives, while tabulate_basis_all and
    # tabulate_basis_derivatives_all return data including all
    # possible components and derivatives. We therefore need to
    # iterate over prefixes (= elements), call tabulate_basis_all or
    # tabulate_basis_derivatives all, and then extract the relevant
    # data and fill in the psi_tables. We therefore need to extract
    # for each prefix, which tables need to be filled in.

    # For each unique prefix, check which derivatives and components
    # are used
    used_derivatives_and_components = {}
    for prefix in prefixes:
        used_derivatives_and_components[prefix] = {}
        for table in psi_tables:
            if prefix not in table:
                continue

            # Check for derivative
            if "_D" in table:
                d = table.split("_D")[1].split("_")[0]
                n = sum([int(_d) for _d in d])  # FIXME: Assume at most 9 derivatives...
            else:
                n = 0

            # Check for component
            if "_C" in table:
                c = table.split("_C")[1].split("_")[0]
            else:
                c = None

            # Note that derivative has been used
            if n not in used_derivatives_and_components[prefix]:
                used_derivatives_and_components[prefix][n] = set()
            used_derivatives_and_components[prefix][n].add(c)

    # Generate code for setting quadrature weights
    code += [f_comment("Set quadrature weights")]
    code += [f_declaration("const double*", "W", "quadrature_weights")]
    code += [""]

    # Generate code for calling evaluate_basis_[derivatives_]all
    for prefix in prefixes:

        # Get element data for current element
        counter = int(prefix.split("FE")[1])
        space_dim = element_data[counter]["num_element_dofs"]
        value_size = element_data[counter]["physical_value_size"]
        element_classname = element_data[counter]["classname"]

        # Iterate over derivative orders
        for n, components in sorted_by_key(used_derivatives_and_components[prefix]):
            # components are a set and need to be sorted
            components = sorted(components)

            # Code for evaluate_basis_all (n = 0 which means it's not
            # a derivative)
            if n == 0:

                code += [f_comment("--- Evaluation of basis functions ---")]
                code += [""]

                # Compute variables for code generation
                eval_stride = value_size
                eval_size = space_dim * eval_stride
                table_size = num_cells * space_dim

                # Iterate over components and initialize tables
                for c in components:

                    # Set name of table
                    if c is None:
                        table_name = prefix
                    else:
                        table_name = prefix + "_C%s" % c

                    # Generate code for declaration of table
                    code += [f_comment("Create table %s for basis function values on all cells" % table_name)]
                    code += [f_eval_basis_decl % {"table_name": table_name}]
                    code += [f_eval_basis_init % {"table_name": table_name,
                                                  "table_size": table_size}]
                    code += [""]

                # Iterate over cells in macro element and evaluate basis
                for cell_number in range(num_cells):

                    # Compute variables for code generation
                    eval_name = "%s_values_%d" % (prefix, cell_number)
                    table_offset = cell_number * space_dim
                    vertex_offset = cell_number * num_vertices * gdim

                    # Generate block of code for loop
                    block = []

                    # Generate code for calling evaluate_basis_all
                    block += [f_eval_basis % {"classname": element_classname,
                                              "eval_name": eval_name,
                                              "gdim": gdim,
                                              "vertex_offset": vertex_offset}]

                    # Iterate over components and extract values
                    for c in components:

                        # Set name of table and component offset
                        if c is None:
                            table_name = prefix
                            eval_offset = 0
                        else:
                            table_name = prefix + "_C%s" % c
                            eval_offset = int(c)

                        # Generate code for copying values
                        block += [""]
                        block += [f_eval_basis_copy % {"table_name": table_name,
                                                       "eval_name": eval_name,
                                                       "eval_stride": eval_stride,
                                                       "eval_offset": eval_offset,
                                                       "space_dim": space_dim,
                                                       "table_offset": table_offset}]

                    # Generate code
                    code += [f_comment("Evaluate basis functions on cell %d" % cell_number)]
                    code += [f_static_array("double", eval_name, eval_size)]
                    code += f_loop(block, [("ip", 0, "num_quadrature_points")])
                    code += [""]

            # Code for evaluate_basis_derivatives_all (derivative of degree n > 0)
            else:

                code += [f_comment("--- Evaluation of basis function derivatives of order %d ---" % n)]
                code += [""]

                # FIXME: We extract values for all possible
                # derivatives, even
                # FIXME: if not all are used. (For components, we
                # extract only
                # FIXME: components that are actually used.) This may
                # be optimized
                # FIXME: but the extra cost is likely small.

                # Get derivative tuples
                __, deriv_tuples = compute_derivative_tuples(n, gdim)

                # Generate names for derivatives
                derivs = ["".join(str(_d) for _d in d) for d in deriv_tuples]

                # Compute variables for code generation
                eval_stride = value_size * len(derivs)
                eval_size = space_dim * eval_stride
                table_size = num_cells * space_dim

                # Iterate over derivatives and initialize tables
                seen_derivs = set()
                for d in derivs:

                    # Skip derivative if seen before (d^2/dxdy = d^2/dydx)
                    if d in seen_derivs:
                        continue
                    seen_derivs.add(d)

                    # Iterate over components
                    for c in components:

                        # Set name of table
                        if c is None:
                            table_name = prefix + "_D%s" % d
                        else:
                            table_name = prefix + "_C%s_D%s" % (c, d)

                        # Generate code for declaration of table
                        code += [f_comment("Create table %s for basis function derivatives on all cells" % table_name)]
                        code += [(f_eval_derivs_decl % {"table_name": table_name})]
                        code += [(f_eval_derivs_init % {"table_name": table_name,
                                                        "table_size": table_size})]
                        code += [""]

                # Iterate over cells (in macro element)
                for cell_number in range(num_cells):

                    # Compute variables for code generation
                    eval_name = "%s_dvalues_%d_%d" % (prefix, n, cell_number)
                    table_offset = cell_number * space_dim
                    vertex_offset = cell_number * num_vertices * gdim

                    # Generate block of code for loop
                    block = []

                    # Generate code for calling evaluate_basis_derivatives_all
                    block += [f_eval_derivs % {"classname": element_classname,
                                               "eval_name": eval_name,
                                               "gdim": gdim,
                                               "vertex_offset": vertex_offset,
                                               "n": n}]

                    # Iterate over derivatives and extract values
                    seen_derivs = set()
                    for i, d in enumerate(derivs):

                        # Skip derivative if seen before (d^2/dxdy = d^2/dydx)
                        if d in seen_derivs:
                            continue
                        seen_derivs.add(d)

                        # Iterate over components
                        for c in components:

                            # Set name of table and component offset
                            if c is None:
                                table_name = prefix + "_D%s" % d
                                eval_offset = i
                            else:
                                table_name = prefix + "_C%s_D%s" % (c, d)
                                eval_offset = len(derivs) * int(c) + i

                            # Generate code for copying values
                            block += [""]
                            block += [(f_eval_derivs_copy % {"table_name": table_name,
                                                             "eval_name": eval_name,
                                                             "eval_stride": eval_stride,
                                                             "eval_offset": eval_offset,
                                                             "space_dim": space_dim,
                                                             "table_offset": table_offset})]

                    # Generate code
                    code += [f_comment("Evaluate basis function derivatives on cell %d" % cell_number)]
                    code += [f_static_array("double", eval_name, eval_size)]
                    code += f_loop(block, [("ip", 0, "num_quadrature_points")])
                    code += [""]

                # Add newline
                code += [""]

    return code
Beispiel #31
0
def interpret_ufl_namespace(namespace):
    "Takes a namespace dict from an executed ufl file and converts it to a FileData object."
    # Object to hold all returned data
    ufd = FileData()

    # Extract object names for Form, Coefficient and FiniteElementBase objects
    # The use of id(obj) as key in object_names is necessary
    # because we need to distinguish between instances,
    # and not just between objects with different values.
    for name, value in sorted_by_key(namespace):
        # Store objects by reserved name OR instance id
        reserved_names = ("unknown",)  # Currently only one reserved name
        if name in reserved_names:
            # Store objects with reserved names
            ufd.reserved_objects[name] = value
            # FIXME: Remove after FFC is updated to use reserved_objects:
            ufd.object_names[name] = value
            ufd.object_by_name[name] = value
        elif isinstance(value, (FiniteElementBase, Coefficient, Argument, Form, Expr)):
            # Store instance <-> name mappings for important objects
            # without a reserved name
            ufd.object_names[id(value)] = name
            ufd.object_by_name[name] = value

    # Get list of exported forms
    forms = namespace.get("forms")
    if forms is None:
        # Get forms from object_by_name, which has already mapped
        # tuple->Form where needed
        def get_form(name):
            form = ufd.object_by_name.get(name)
            return form if isinstance(form, Form) else None
        a_form = get_form("a")
        L_form = get_form("L")
        M_form = get_form("M")
        forms = [a_form, L_form, M_form]
        # Add forms F and J if not "a" and "L" are used
        if a_form is None or L_form is None:
            F_form = get_form("F")
            J_form = get_form("J")
            forms += [F_form, J_form]
        # Remove Nones
        forms = [f for f in forms if isinstance(f, Form)]
    ufd.forms = forms

    # Validate types
    if not isinstance(ufd.forms, (list, tuple)):
        error("Expecting 'forms' to be a list or tuple, not '%s'." % type(ufd.forms))
    if not all(isinstance(a, Form) for a in ufd.forms):
        error("Expecting 'forms' to be a list of Form instances.")

    # Get list of exported elements
    elements = namespace.get("elements")
    if elements is None:
        elements = [ufd.object_by_name.get(name) for name in ("element",)]
        elements = [e for e in elements if e is not None]
    ufd.elements = elements

    # Validate types
    if not isinstance(ufd.elements, (list, tuple)):
        error("Expecting 'elements' to be a list or tuple, not '%s'." % type(ufd.elements))
    if not all(isinstance(e, FiniteElementBase) for e in ufd.elements):
        error("Expecting 'elements' to be a list of FiniteElementBase instances.")

    # Get list of exported coefficients
    # TODO: Temporarily letting 'coefficients' override 'functions',
    # but allow 'functions' for compatibility
    functions = namespace.get("functions", [])
    if functions:
        warning("Deprecation warning: Rename 'functions' to 'coefficients' to export coefficients.")
    ufd.coefficients = namespace.get("coefficients", functions)

    # Validate types
    if not isinstance(ufd.coefficients, (list, tuple)):
        error("Expecting 'coefficients' to be a list or tuple, not '%s'." % type(ufd.coefficients))
    if not all(isinstance(e, Coefficient) for e in ufd.coefficients):
        error("Expecting 'coefficients' to be a list of Coefficient instances.")

    # Get list of exported expressions
    ufd.expressions = namespace.get("expressions", [])

    # Validate types
    if not isinstance(ufd.expressions, (list, tuple)):
        error("Expecting 'expressions' to be a list or tuple, not '%s'." % type(ufd.expressions))
    if not all(isinstance(e, Expr) for e in ufd.expressions):
        error("Expecting 'expressions' to be a list of Expr instances.")

    # Return file data
    return ufd
Beispiel #32
0
 def tabulate(self, order, points):
     result = self._element.tabulate(order, points)
     extracted = {}
     for (dtuple, values) in sorted_by_key(result):
         extracted[dtuple] = numpy.array([values[i] for i in self._indices])
     return extracted
Beispiel #33
0
    def reduce_ops(self):
        "Reduce the number of operations needed to evaluate the sum."

        if self._reduced:
            return self._reduced
        # NOTE: Assuming that sum has already been expanded.
        # TODO: Add test for this and handle case if it is not.

        # TODO: The entire function looks expensive, can it be optimised?

        # TODO: It is not necessary to create a new Sum if we do not
        # have more than one Fraction.

        # First group all fractions in the sum.
        new_sum = _group_fractions(self)
        if new_sum._prec != 3:  # sum
            self._reduced = new_sum.reduce_ops()
            return self._reduced
        # Loop all variables of the sum and collect the number of
        # common variables that can be factored out.
        common_vars = {}
        for var in new_sum.vrs:
            # Get dictonary of occurrences and add the variable and
            # the number of occurrences to common dictionary.
            for k, v in sorted_by_key(var.get_var_occurrences()):
                if k in common_vars:
                    common_vars[k].append((v, var))
                else:
                    common_vars[k] = [(v, var)]

        # Determine the maximum reduction for each variable sorted as:
        # {(x*x*y, x*y*z, 2*y):[2, [y]]}.
        terms_reductions = {}
        for k, v in sorted_by_key(common_vars):
            # If the number of expressions that can be reduced is only
            # one there is nothing to be done.
            if len(v) > 1:
                # TODO: Is there a better way to compute the reduction
                # gain and the number of occurrences we should remove?

                # Get the list of number of occurences of 'k' in
                # expressions in 'v'.
                occurrences = [t[0] for t in v]

                # Determine the favorable number of occurences and an
                # estimate of the maximum reduction for current
                # variable.
                fav_occur = 0
                reduc = 0
                for i in set(occurrences):
                    # Get number of terms that has a number of
                    # occcurences equal to or higher than the current
                    # number.
                    num_terms = len([o for o in occurrences if o >= i])

                    # An estimate of the reduction in operations is:
                    # (number_of_terms - 1) * number_occurrences.
                    new_reduc = (num_terms - 1) * i
                    if new_reduc > reduc:
                        reduc = new_reduc
                        fav_occur = i

                # Extract the terms of v where the number of
                # occurrences is equal to or higher than the most
                # favorable number of occurrences.
                terms = sorted([t[1] for t in v if t[0] >= fav_occur])

                # We need to reduce the expression with the favorable
                # number of occurrences of the current variable.
                red_vars = [k] * fav_occur

                # If the list of terms is already present in the
                # dictionary, add the reduction count and the
                # variables.
                if tuple(terms) in terms_reductions:
                    terms_reductions[tuple(terms)][0] += reduc
                    terms_reductions[tuple(terms)][1] += red_vars
                else:
                    terms_reductions[tuple(terms)] = [reduc, red_vars]

        if terms_reductions:
            # Invert dictionary of terms.
            reductions_terms = dict([((v[0], tuple(v[1])), k) for k,
                                     v in terms_reductions.items()])

            # Create a sorted list of those variables that give the
            # highest reduction.
            sorted_reduc_var = sorted(reductions_terms.keys(),
                                      reverse=True)

            # Create a new dictionary of terms that should be reduced,
            # if some terms overlap, only pick the one which give the
            # highest reduction to ensure that a*x*x + b*x*x + x*x*y +
            # 2*y -> x*x*(a + b + y) + 2*y NOT x*x*(a + b) + y*(2 +
            # x*x).
            reduction_vars = {}
            rejections = {}
            for var in sorted_reduc_var:
                terms = reductions_terms[var]
                if _overlap(terms, reduction_vars) or _overlap(terms,
                                                               rejections):
                    rejections[var[1]] = terms
                else:
                    reduction_vars[var[1]] = terms

            # Reduce each set of terms with appropriate variables.
            all_reduced_terms = []
            reduced_expressions = []
            for reduc_var, terms in sorted(reduction_vars.items()):

                # Add current terms to list of all variables that have
                # been reduced.
                all_reduced_terms += list(terms)

                # Create variable that we will use to reduce the terms.
                reduction_var = None
                if len(reduc_var) > 1:
                    reduction_var = create_product(list(reduc_var))
                else:
                    reduction_var = reduc_var[0]

                # Reduce all terms that need to be reduced.
                reduced_terms = [t.reduce_var(reduction_var) for t in terms]

                # Create reduced expression.
                reduced_expr = None
                if len(reduced_terms) > 1:
                    # Try to reduce the reduced terms further.
                    reduced_expr = create_product([reduction_var,
                                                   create_sum(reduced_terms).reduce_ops()])
                else:
                    reduced_expr = create_product(reduction_var,
                                                  reduced_terms[0])

                # Add reduced expression to list of reduced
                # expressions.
                reduced_expressions.append(reduced_expr)

            # Create list of terms that should not be reduced.
            dont_reduce_terms = []
            for v in new_sum.vrs:
                if v not in all_reduced_terms:
                    dont_reduce_terms.append(v)

            # Create expression from terms that was not reduced.
            not_reduced_expr = None
            if dont_reduce_terms and len(dont_reduce_terms) > 1:
                # Try to reduce the remaining terms that were not
                # reduced at first.
                not_reduced_expr = create_sum(dont_reduce_terms).reduce_ops()
            elif dont_reduce_terms:
                not_reduced_expr = dont_reduce_terms[0]

            # Create return expression.
            if not_reduced_expr:
                self._reduced = create_sum(reduced_expressions + [not_reduced_expr])
            elif len(reduced_expressions) > 1:
                self._reduced = create_sum(reduced_expressions)
            else:
                self._reduced = reduced_expressions[0]

            return self._reduced

        # Return self if we don't have any variables for which we can
        # reduce the sum.
        self._reduced = self
        return self._reduced
Beispiel #34
0
def group_vars(expr, format):
    """Group variables in an expression, such that:
    "x + y + z + 2*y + 6*z" = "x + 3*y + 7*z"
    "x*x + x*x + 2*x + 3*x + 5" = "2.0*x*x + 5.0*x + 5"
    "x*y + y*x + 2*x*y + 3*x + 0*x + 5" = "5.0*x*y + 3.0*x + 5"
    "(y + z)*x + 5*(y + z)*x" = "6.0*(y + z)*x"
    "1/(x*x) + 2*1/(x*x) + std::sqrt(x) + 6*std::sqrt(x)" = "3*1/(x*x) + 7*std::sqrt(x)"
    """

    # Get formats
    format_float = format["floating point"]
    add   = format["add"](["", ""])
    mult  = format["multiply"](["", ""])

    new_prods = {}

    # Get list of products
    prods = split_expression(expr, format, add)

    # Loop products and collect factors
    for p in prods:
        # Get list of variables, and do a basic sort
        vrs = split_expression(p, format, mult)
        factor = 1
        new_var = []

        # Try to multiply factor with variable, else variable must be multiplied by factor later
        # If we don't have a variable, set factor to zero and break
        for v in vrs:
            if v:
                try:
                    f = float(v)
                    factor *= f
                except:
                    new_var.append(v)
            else:
                factor = 0
                break

        # Create new variable that must be multiplied with factor. Add this
        # variable to dictionary, if it already exists add factor to other factors
        new_var.sort()
        new_var = mult.join(new_var)
        if new_var in new_prods:
            new_prods[new_var] += factor
        else:
            new_prods[new_var] = factor

    # Reset products
    prods = []
    for prod, f in sorted_by_key(new_prods):
        # If we have a product append mult of both
        if prod:
            # If factor is 1.0 we don't need it
            if f == 1.0:
                prods.append(prod)
            else:
                prods.append(mult.join([format_float(f), prod]))
        # If we just have a factor
        elif f:
            prods.append(format_float(f))

    prods.sort()
    return add.join(prods)
Beispiel #35
0
    def reduce_ops(self):
        "Reduce the number of operations needed to evaluate the sum."
        #        global ind
        #        ind += " "
        #        print "\n%sreduce_ops, start" % ind

        if self._reduced:
            return self._reduced
        # NOTE: Assuming that sum has already been expanded.
        # TODO: Add test for this and handle case if it is not.

        # TODO: The entire function looks expensive, can it be optimised?

        # TODO: It is not necessary to create a new Sum if we do not have more
        # than one Fraction.
        # First group all fractions in the sum.
        new_sum = _group_fractions(self)
        if new_sum._prec != 3:  # sum
            self._reduced = new_sum.reduce_ops()
            return self._reduced
        # Loop all variables of the sum and collect the number of common
        # variables that can be factored out.
        common_vars = {}
        for var in new_sum.vrs:
            # Get dictonary of occurrences and add the variable and the number
            # of occurrences to common dictionary.
            for k, v in sorted_by_key(var.get_var_occurrences()):
                #                print
                #                print ind + "var: ", var
                #                print ind + "k: ", k
                #                print ind + "v: ", v
                if k in common_vars:
                    common_vars[k].append((v, var))
                else:
                    common_vars[k] = [(v, var)]
#        print
#        print "common vars: "
#        for k,v in common_vars.items():
#            print "k: ", k
#            print "v: ", v
#        print
# Determine the maximum reduction for each variable
# sorted as: {(x*x*y, x*y*z, 2*y):[2, [y]]}.
        terms_reductions = {}
        for k, v in sorted_by_key(common_vars):
            #            print
            #            print ind + "k: ", k
            #            print ind + "v: ", v
            # If the number of expressions that can be reduced is only one
            # there is nothing to be done.
            if len(v) > 1:
                # TODO: Is there a better way to compute the reduction gain
                # and the number of occurrences we should remove?

                # Get the list of number of occurences of 'k' in expressions
                # in 'v'.
                occurrences = [t[0] for t in v]

                # Determine the favorable number of occurences and an estimate
                # of the maximum reduction for current variable.
                fav_occur = 0
                reduc = 0
                for i in set(occurrences):
                    # Get number of terms that has a number of occcurences equal
                    # to or higher than the current number.
                    num_terms = len([o for o in occurrences if o >= i])

                    # An estimate of the reduction in operations is:
                    # (number_of_terms - 1) * number_occurrences.
                    new_reduc = (num_terms - 1) * i
                    if new_reduc > reduc:
                        reduc = new_reduc
                        fav_occur = i

                # Extract the terms of v where the number of occurrences is
                # equal to or higher than the most favorable number of occurrences.
                terms = sorted([t[1] for t in v if t[0] >= fav_occur])

                # We need to reduce the expression with the favorable number of
                # occurrences of the current variable.
                red_vars = [k] * fav_occur

                # If the list of terms is already present in the dictionary,
                # add the reduction count and the variables.
                if tuple(terms) in terms_reductions:
                    terms_reductions[tuple(terms)][0] += reduc
                    terms_reductions[tuple(terms)][1] += red_vars
                else:
                    terms_reductions[tuple(terms)] = [reduc, red_vars]
#        print "\nterms_reductions: "
#        for k,v in terms_reductions.items():
#            print "k: ", create_sum(k)
#            print "v: ", v
#        print "red: self: ", self
        if terms_reductions:
            # Invert dictionary of terms.
            reductions_terms = dict([
                ((v[0], tuple(v[1])), k)
                for k, v in six.iteritems(terms_reductions)
            ])

            # Create a sorted list of those variables that give the highest
            # reduction.
            sorted_reduc_var = sorted(six.iterkeys(reductions_terms),
                                      reverse=True)
            #            sorted_reduc_var = [k for k, v in six.iteritems(reductions_terms)]
            #            print
            #            print ind + "raw"
            #            for k in sorted_reduc_var:
            #                print ind, k[0], k[1]
            #            sorted_reduc_var.sort()
            #            sorted_reduc_var.sort(lambda x, y: cmp(x[0], y[0]))
            #            sorted_reduc_var.reverse()
            #            print ind + "sorted"
            #            for k in sorted_reduc_var:
            #                print ind, k[0], k[1]

            # Create a new dictionary of terms that should be reduced, if some
            # terms overlap, only pick the one which give the highest reduction to
            # ensure that a*x*x + b*x*x + x*x*y + 2*y -> x*x*(a + b + y) + 2*y NOT
            # x*x*(a + b) + y*(2 + x*x).
            reduction_vars = {}
            rejections = {}
            for var in sorted_reduc_var:
                terms = reductions_terms[var]
                if _overlap(terms, reduction_vars) or _overlap(
                        terms, rejections):
                    rejections[var[1]] = terms
                else:
                    reduction_vars[var[1]] = terms

#            print "\nreduction_vars: "
#            for k,v in reduction_vars.items():
#                print "k: ", k
#                print "v: ", v

# Reduce each set of terms with appropriate variables.
            all_reduced_terms = []
            reduced_expressions = []
            for reduc_var, terms in sorted(six.iteritems(reduction_vars)):

                # Add current terms to list of all variables that have been reduced.
                all_reduced_terms += list(terms)

                # Create variable that we will use to reduce the terms.
                reduction_var = None
                if len(reduc_var) > 1:
                    reduction_var = create_product(list(reduc_var))
                else:
                    reduction_var = reduc_var[0]

                # Reduce all terms that need to be reduced.
                reduced_terms = [t.reduce_var(reduction_var) for t in terms]

                # Create reduced expression.
                reduced_expr = None
                if len(reduced_terms) > 1:
                    # Try to reduce the reduced terms further.
                    reduced_expr = create_product([
                        reduction_var,
                        create_sum(reduced_terms).reduce_ops()
                    ])
                else:
                    reduced_expr = create_product(reduction_var,
                                                  reduced_terms[0])

                # Add reduced expression to list of reduced expressions.
                reduced_expressions.append(reduced_expr)

            # Create list of terms that should not be reduced.
            dont_reduce_terms = []
            for v in new_sum.vrs:
                if not v in all_reduced_terms:
                    dont_reduce_terms.append(v)

            # Create expression from terms that was not reduced.
            not_reduced_expr = None
            if dont_reduce_terms and len(dont_reduce_terms) > 1:
                # Try to reduce the remaining terms that were not reduced at first.
                not_reduced_expr = create_sum(dont_reduce_terms).reduce_ops()
            elif dont_reduce_terms:
                not_reduced_expr = dont_reduce_terms[0]

            # Create return expression.
            if not_reduced_expr:
                self._reduced = create_sum(reduced_expressions +
                                           [not_reduced_expr])
            elif len(reduced_expressions) > 1:
                self._reduced = create_sum(reduced_expressions)
            else:
                self._reduced = reduced_expressions[0]


#            # NOTE: Only switch on for debugging.
#            if not self._reduced.expand() == self.expand():
#                print reduced_expressions[0]
#                print reduced_expressions[0].expand()
#                print "self: ", self
#                print "red:  ", repr(self._reduced)
#                print "self.exp: ", self.expand()
#                print "red.exp:  ", self._reduced.expand()
#                error("Reduced expression is not equal to original expression.")
            return self._reduced

        # Return self if we don't have any variables for which we can reduce
        # the sum.
        self._reduced = self
        return self._reduced
Beispiel #36
0
    def expand(self):
        "Expand all members of the sum."

        # If sum is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # TODO: This function might need some optimisation.

        # Sort variables into symbols, products and fractions (add floats
        # directly to new list, will be handled later). Add fractions if
        # possible else add to list.
        new_variables = []
        syms = []
        prods = []
        frac_groups = {}
        # TODO: Rather than using '+', would it be more efficient to collect
        # the terms first?
        for var in self.vrs:
            exp = var.expand()
            # TODO: Should we also group fractions, or put this in a separate function?
            if exp._prec in (0, 4):  # float or frac
                new_variables.append(exp)
            elif exp._prec == 1:  # sym
                syms.append(exp)
            elif exp._prec == 2:  # prod
                prods.append(exp)
            elif exp._prec == 3:  # sum
                for v in exp.vrs:
                    if v._prec in (0, 4):  # float or frac
                        new_variables.append(v)
                    elif v._prec == 1:  # sym
                        syms.append(v)
                    elif v._prec == 2:  # prod
                        prods.append(v)

        # Sort all variables in groups: [2*x, -7*x], [(x + y), (2*x + 4*y)] etc.
        # First handle product in order to add symbols if possible.
        prod_groups = {}
        for v in prods:
            if v.get_vrs() in prod_groups:
                prod_groups[v.get_vrs()] += v
            else:
                prod_groups[v.get_vrs()] = v

        sym_groups = {}
        # Loop symbols and add to appropriate groups.
        for v in syms:
            # First try to add to a product group.
            if (v, ) in prod_groups:
                prod_groups[(v, )] += v
            # Then to other symbols.
            elif v in sym_groups:
                sym_groups[v] += v
            # Create a new entry in the symbols group.
            else:
                sym_groups[v] = v

        # Loop groups and add to new variable list.
        for k, v in sorted_by_key(sym_groups):
            new_variables.append(v)
        for k, v in sorted_by_key(prod_groups):
            new_variables.append(v)
#        for k,v in frac_groups.iteritems():
#            new_variables.append(v)
#            append(v)

        if len(new_variables) > 1:
            # Return new sum (will remove multiple instances of floats during construction).
            self._expanded = create_sum(sorted(new_variables))
            return self._expanded
        elif new_variables:
            # If we just have one variable left, return it since it is already expanded.
            self._expanded = new_variables[0]
            return self._expanded
        error("Where did the variables go?")
Beispiel #37
0
def _evaluate_basis_at_quadrature_points(psi_tables, gdim, element_data,
                                         form_prefix, num_vertices, num_cells):
    "Generate code for calling evaluate basis (derivatives) at quadrature points"

    # Prefetch formats to speed up code generation
    f_comment = format["comment"]
    f_declaration = format["declaration"]
    f_static_array = format["static array"]
    f_loop = format["generate loop"]
    f_eval_basis_decl = format["eval_basis_decl"]
    f_eval_basis_init = format["eval_basis_init"]
    f_eval_basis = format["eval_basis"]
    f_eval_basis_copy = format["eval_basis_copy"]
    f_eval_derivs_decl = format["eval_derivs_decl"]
    f_eval_derivs_init = format["eval_derivs_init"]
    f_eval_derivs = format["eval_derivs"]
    f_eval_derivs_copy = format["eval_derivs_copy"]

    code = []

    # Extract prefixes for tables
    prefixes = sorted(set(table.split("_")[0] for table in psi_tables))

    # Use lower case prefix for form name
    form_prefix = form_prefix.lower()

    # The psi_tables used by the quadrature code are for scalar
    # components of specific derivatives, while tabulate_basis_all and
    # tabulate_basis_derivatives_all return data including all
    # possible components and derivatives. We therefore need to
    # iterate over prefixes (= elements), call tabulate_basis_all or
    # tabulate_basis_derivatives all, and then extract the relevant
    # data and fill in the psi_tables. We therefore need to extract
    # for each prefix, which tables need to be filled in.

    # For each unique prefix, check which derivatives and components
    # are used
    used_derivatives_and_components = {}
    for prefix in prefixes:
        used_derivatives_and_components[prefix] = {}
        for table in psi_tables:
            if prefix not in table:
                continue

            # Check for derivative
            if "_D" in table:
                d = table.split("_D")[1].split("_")[0]
                n = sum([int(_d) for _d in d
                         ])  # FIXME: Assume at most 9 derivatives...
            else:
                n = 0

            # Check for component
            if "_C" in table:
                c = table.split("_C")[1].split("_")[0]
            else:
                c = None

            # Note that derivative has been used
            if n not in used_derivatives_and_components[prefix]:
                used_derivatives_and_components[prefix][n] = set()
            used_derivatives_and_components[prefix][n].add(c)

    # Generate code for setting quadrature weights
    code += [f_comment("Set quadrature weights")]
    code += [f_declaration("const double*", "W", "quadrature_weights")]
    code += [""]

    # Generate code for calling evaluate_basis_[derivatives_]all
    for prefix in prefixes:

        # Get element data for current element
        counter = int(prefix.split("FE")[1])
        space_dim = element_data[counter]["num_element_dofs"]
        value_size = element_data[counter]["physical_value_size"]
        element_classname = element_data[counter]["classname"]

        # Iterate over derivative orders
        for n, components in sorted_by_key(
                used_derivatives_and_components[prefix]):
            # components are a set and need to be sorted
            components = sorted(components)

            # Code for evaluate_basis_all (n = 0 which means it's not
            # a derivative)
            if n == 0:

                code += [f_comment("--- Evaluation of basis functions ---")]
                code += [""]

                # Compute variables for code generation
                eval_stride = value_size
                eval_size = space_dim * eval_stride
                table_size = num_cells * space_dim

                # Iterate over components and initialize tables
                for c in components:

                    # Set name of table
                    if c is None:
                        table_name = prefix
                    else:
                        table_name = prefix + "_C%s" % c

                    # Generate code for declaration of table
                    code += [
                        f_comment(
                            "Create table %s for basis function values on all cells"
                            % table_name)
                    ]
                    code += [f_eval_basis_decl % {"table_name": table_name}]
                    code += [
                        f_eval_basis_init % {
                            "table_name": table_name,
                            "table_size": table_size
                        }
                    ]
                    code += [""]

                # Iterate over cells in macro element and evaluate basis
                for cell_number in range(num_cells):

                    # Compute variables for code generation
                    eval_name = "%s_values_%d" % (prefix, cell_number)
                    table_offset = cell_number * space_dim
                    vertex_offset = cell_number * num_vertices * gdim

                    # Generate block of code for loop
                    block = []

                    # Generate code for calling evaluate_basis_all
                    block += [
                        f_eval_basis % {
                            "classname": element_classname,
                            "eval_name": eval_name,
                            "gdim": gdim,
                            "vertex_offset": vertex_offset
                        }
                    ]

                    # Iterate over components and extract values
                    for c in components:

                        # Set name of table and component offset
                        if c is None:
                            table_name = prefix
                            eval_offset = 0
                        else:
                            table_name = prefix + "_C%s" % c
                            eval_offset = int(c)

                        # Generate code for copying values
                        block += [""]
                        block += [
                            f_eval_basis_copy % {
                                "table_name": table_name,
                                "eval_name": eval_name,
                                "eval_stride": eval_stride,
                                "eval_offset": eval_offset,
                                "space_dim": space_dim,
                                "table_offset": table_offset
                            }
                        ]

                    # Generate code
                    code += [
                        f_comment("Evaluate basis functions on cell %d" %
                                  cell_number)
                    ]
                    code += [f_static_array("double", eval_name, eval_size)]
                    code += f_loop(block, [("ip", 0, "num_quadrature_points")])
                    code += [""]

            # Code for evaluate_basis_derivatives_all (derivative of degree n > 0)
            else:

                code += [
                    f_comment(
                        "--- Evaluation of basis function derivatives of order %d ---"
                        % n)
                ]
                code += [""]

                # FIXME: We extract values for all possible
                # derivatives, even
                # FIXME: if not all are used. (For components, we
                # extract only
                # FIXME: components that are actually used.) This may
                # be optimized
                # FIXME: but the extra cost is likely small.

                # Get derivative tuples
                __, deriv_tuples = compute_derivative_tuples(n, gdim)

                # Generate names for derivatives
                derivs = ["".join(str(_d) for _d in d) for d in deriv_tuples]

                # Compute variables for code generation
                eval_stride = value_size * len(derivs)
                eval_size = space_dim * eval_stride
                table_size = num_cells * space_dim

                # Iterate over derivatives and initialize tables
                seen_derivs = set()
                for d in derivs:

                    # Skip derivative if seen before (d^2/dxdy = d^2/dydx)
                    if d in seen_derivs:
                        continue
                    seen_derivs.add(d)

                    # Iterate over components
                    for c in components:

                        # Set name of table
                        if c is None:
                            table_name = prefix + "_D%s" % d
                        else:
                            table_name = prefix + "_C%s_D%s" % (c, d)

                        # Generate code for declaration of table
                        code += [
                            f_comment(
                                "Create table %s for basis function derivatives on all cells"
                                % table_name)
                        ]
                        code += [(f_eval_derivs_decl % {
                            "table_name": table_name
                        })]
                        code += [(f_eval_derivs_init % {
                            "table_name": table_name,
                            "table_size": table_size
                        })]
                        code += [""]

                # Iterate over cells (in macro element)
                for cell_number in range(num_cells):

                    # Compute variables for code generation
                    eval_name = "%s_dvalues_%d_%d" % (prefix, n, cell_number)
                    table_offset = cell_number * space_dim
                    vertex_offset = cell_number * num_vertices * gdim

                    # Generate block of code for loop
                    block = []

                    # Generate code for calling evaluate_basis_derivatives_all
                    block += [
                        f_eval_derivs % {
                            "classname": element_classname,
                            "eval_name": eval_name,
                            "gdim": gdim,
                            "vertex_offset": vertex_offset,
                            "n": n
                        }
                    ]

                    # Iterate over derivatives and extract values
                    seen_derivs = set()
                    for i, d in enumerate(derivs):

                        # Skip derivative if seen before (d^2/dxdy = d^2/dydx)
                        if d in seen_derivs:
                            continue
                        seen_derivs.add(d)

                        # Iterate over components
                        for c in components:

                            # Set name of table and component offset
                            if c is None:
                                table_name = prefix + "_D%s" % d
                                eval_offset = i
                            else:
                                table_name = prefix + "_C%s_D%s" % (c, d)
                                eval_offset = len(derivs) * int(c) + i

                            # Generate code for copying values
                            block += [""]
                            block += [(f_eval_derivs_copy % {
                                "table_name": table_name,
                                "eval_name": eval_name,
                                "eval_stride": eval_stride,
                                "eval_offset": eval_offset,
                                "space_dim": space_dim,
                                "table_offset": table_offset
                            })]

                    # Generate code
                    code += [
                        f_comment(
                            "Evaluate basis function derivatives on cell %d" %
                            cell_number)
                    ]
                    code += [f_static_array("double", eval_name, eval_size)]
                    code += f_loop(block, [("ip", 0, "num_quadrature_points")])
                    code += [""]

                # Add newline
                code += [""]

    return code
Beispiel #38
0
    def reduce_ops(self):
        "Reduce the number of operations needed to evaluate the sum."

        if self._reduced:
            return self._reduced
        # NOTE: Assuming that sum has already been expanded.
        # TODO: Add test for this and handle case if it is not.

        # TODO: The entire function looks expensive, can it be optimised?

        # TODO: It is not necessary to create a new Sum if we do not
        # have more than one Fraction.

        # First group all fractions in the sum.
        new_sum = _group_fractions(self)
        if new_sum._prec != 3:  # sum
            self._reduced = new_sum.reduce_ops()
            return self._reduced
        # Loop all variables of the sum and collect the number of
        # common variables that can be factored out.
        common_vars = {}
        for var in new_sum.vrs:
            # Get dictonary of occurrences and add the variable and
            # the number of occurrences to common dictionary.
            for k, v in sorted_by_key(var.get_var_occurrences()):
                if k in common_vars:
                    common_vars[k].append((v, var))
                else:
                    common_vars[k] = [(v, var)]

        # Determine the maximum reduction for each variable sorted as:
        # {(x*x*y, x*y*z, 2*y):[2, [y]]}.
        terms_reductions = {}
        for k, v in sorted_by_key(common_vars):
            # If the number of expressions that can be reduced is only
            # one there is nothing to be done.
            if len(v) > 1:
                # TODO: Is there a better way to compute the reduction
                # gain and the number of occurrences we should remove?

                # Get the list of number of occurences of 'k' in
                # expressions in 'v'.
                occurrences = [t[0] for t in v]

                # Determine the favorable number of occurences and an
                # estimate of the maximum reduction for current
                # variable.
                fav_occur = 0
                reduc = 0
                for i in set(occurrences):
                    # Get number of terms that has a number of
                    # occcurences equal to or higher than the current
                    # number.
                    num_terms = len([o for o in occurrences if o >= i])

                    # An estimate of the reduction in operations is:
                    # (number_of_terms - 1) * number_occurrences.
                    new_reduc = (num_terms - 1) * i
                    if new_reduc > reduc:
                        reduc = new_reduc
                        fav_occur = i

                # Extract the terms of v where the number of
                # occurrences is equal to or higher than the most
                # favorable number of occurrences.
                terms = sorted([t[1] for t in v if t[0] >= fav_occur])

                # We need to reduce the expression with the favorable
                # number of occurrences of the current variable.
                red_vars = [k] * fav_occur

                # If the list of terms is already present in the
                # dictionary, add the reduction count and the
                # variables.
                if tuple(terms) in terms_reductions:
                    terms_reductions[tuple(terms)][0] += reduc
                    terms_reductions[tuple(terms)][1] += red_vars
                else:
                    terms_reductions[tuple(terms)] = [reduc, red_vars]

        if terms_reductions:
            # Invert dictionary of terms.
            reductions_terms = dict([((v[0], tuple(v[1])), k)
                                     for k, v in terms_reductions.items()])

            # Create a sorted list of those variables that give the
            # highest reduction.
            sorted_reduc_var = sorted(reductions_terms.keys(), reverse=True)

            # Create a new dictionary of terms that should be reduced,
            # if some terms overlap, only pick the one which give the
            # highest reduction to ensure that a*x*x + b*x*x + x*x*y +
            # 2*y -> x*x*(a + b + y) + 2*y NOT x*x*(a + b) + y*(2 +
            # x*x).
            reduction_vars = {}
            rejections = {}
            for var in sorted_reduc_var:
                terms = reductions_terms[var]
                if _overlap(terms, reduction_vars) or _overlap(
                        terms, rejections):
                    rejections[var[1]] = terms
                else:
                    reduction_vars[var[1]] = terms

            # Reduce each set of terms with appropriate variables.
            all_reduced_terms = []
            reduced_expressions = []
            for reduc_var, terms in sorted(reduction_vars.items()):

                # Add current terms to list of all variables that have
                # been reduced.
                all_reduced_terms += list(terms)

                # Create variable that we will use to reduce the terms.
                reduction_var = None
                if len(reduc_var) > 1:
                    reduction_var = create_product(list(reduc_var))
                else:
                    reduction_var = reduc_var[0]

                # Reduce all terms that need to be reduced.
                reduced_terms = [t.reduce_var(reduction_var) for t in terms]

                # Create reduced expression.
                reduced_expr = None
                if len(reduced_terms) > 1:
                    # Try to reduce the reduced terms further.
                    reduced_expr = create_product([
                        reduction_var,
                        create_sum(reduced_terms).reduce_ops()
                    ])
                else:
                    reduced_expr = create_product(reduction_var,
                                                  reduced_terms[0])

                # Add reduced expression to list of reduced
                # expressions.
                reduced_expressions.append(reduced_expr)

            # Create list of terms that should not be reduced.
            dont_reduce_terms = []
            for v in new_sum.vrs:
                if v not in all_reduced_terms:
                    dont_reduce_terms.append(v)

            # Create expression from terms that was not reduced.
            not_reduced_expr = None
            if dont_reduce_terms and len(dont_reduce_terms) > 1:
                # Try to reduce the remaining terms that were not
                # reduced at first.
                not_reduced_expr = create_sum(dont_reduce_terms).reduce_ops()
            elif dont_reduce_terms:
                not_reduced_expr = dont_reduce_terms[0]

            # Create return expression.
            if not_reduced_expr:
                self._reduced = create_sum(reduced_expressions +
                                           [not_reduced_expr])
            elif len(reduced_expressions) > 1:
                self._reduced = create_sum(reduced_expressions)
            else:
                self._reduced = reduced_expressions[0]

            return self._reduced

        # Return self if we don't have any variables for which we can
        # reduce the sum.
        self._reduced = self
        return self._reduced
Beispiel #39
0
    def sum(self, o, *operands):
        #print("Visiting Sum: " + "\noperands: \n" + "\n".join(map(repr, operands)))

        # Prefetch formats to speed up code generation.
        f_group  = format["grouping"]
        f_add    = format["add"]
        f_mult   = format["multiply"]
        f_float  = format["floating point"]
        code = {}

        # Loop operands that has to be summed and sort according to map (j,k).
        for op in operands:
            # If entries does already exist we can add the code, otherwise just
            # dump them in the element tensor.
            for key, val in sorted_by_key(op):
                if key in code:
                    code[key].append(val)
                else:
                    code[key] = [val]

        # Add sums and group if necessary.
        for key, val in sorted_by_key(code):

            # Exclude all zero valued terms from sum
            value = [v for v in val if not v is None]

            if len(value) > 1:
                # NOTE: Since we no longer call expand_indices, the following
                # is needed to prevent the code from exploding for forms like
                # HyperElasticity
                duplications = {}
                for val in value:
                    if val in duplications:
                        duplications[val] += 1
                        continue
                    duplications[val] = 1

                # Add a product for each term that has duplicate code
                expressions = []
                for expr, num_occur in sorted_by_key(duplications):
                    if num_occur > 1:
                        # Pre-multiply expression with number of occurrences
                        expressions.append(f_mult([f_float(num_occur), expr]))
                        continue
                    # Just add expression if there is only one
                    expressions.append(expr)
                ffc_assert(expressions, "Where did the expressions go?")

                if len(expressions) > 1:
                    code[key] = f_group(f_add(expressions))
                    continue
                code[key] = expressions[0]
            else:
                # Check for zero valued sum and delete from code
                # This might result in returning an empty dict, but that should
                # be interpreted as zero by other handlers.
                if not value:
                    del code[key]
                    continue
                code[key] = value[0]

        return code
Beispiel #40
0
def group_form_integrals(form, domains, do_append_everywhere_integrals=True):
    """Group integrals by domain and type, performing canonical simplification.

    :arg form: the :class:`~.Form` to group the integrals of.
    :arg domains: an iterable of :class:`~.Domain`s.
    :returns: A new :class:`~.Form` with gathered integrands.
    """
    # Group integrals by domain and type
    integrals_by_domain_and_type = \
        group_integrals_by_domain_and_type(form.integrals(), domains)

    integrals = []
    for domain in domains:
        for integral_type in ufl.measure.integral_types():
            # Get integrals with this domain and type
            ddt_integrals = integrals_by_domain_and_type.get((domain, integral_type))
            if ddt_integrals is None:
                continue

            # Group integrals by subdomain id, after splitting e.g.
            #   f*dx((1,2)) + g*dx((2,3)) -> f*dx(1) + (f+g)*dx(2) + g*dx(3)
            # (note: before this call, 'everywhere' is a valid subdomain_id,
            # and after this call, 'otherwise' is a valid subdomain_id)
            single_subdomain_integrals = \
                rearrange_integrals_by_single_subdomains(ddt_integrals, do_append_everywhere_integrals)

            for subdomain_id, ss_integrals in sorted_by_key(single_subdomain_integrals):

                # strip the coordinate derivatives from all integrals
                # this yields a list of the form [(coordinate derivative, integral), ...]
                stripped_integrals_and_coordderivs = strip_coordinate_derivatives(ss_integrals)

                # now group the integrals by the coordinate derivative
                def calc_hash(cd):
                    return sum(sum(tuple_elem._ufl_compute_hash_()
                                   for tuple_elem in tuple_) for tuple_ in cd)
                coordderiv_integrals_dict = {}
                for integral, coordderiv in stripped_integrals_and_coordderivs:
                    coordderivhash = calc_hash(coordderiv)
                    if coordderivhash in coordderiv_integrals_dict:
                        coordderiv_integrals_dict[coordderivhash][1].append(integral)
                    else:
                        coordderiv_integrals_dict[coordderivhash] = (coordderiv, [integral])

                # cd_integrals_dict is now a dict of the form
                # { hash: (CoordinateDerivative, [integral, integral, ...]), ... }
                # we can now put the integrals back together and then afterwards
                # apply the CoordinateDerivative again

                for cdhash, samecd_integrals in sorted_by_key(coordderiv_integrals_dict):

                    # Accumulate integrands of integrals that share the
                    # same compiler data
                    integrands_and_cds = \
                        accumulate_integrands_with_same_metadata(samecd_integrals[1])

                    for integrand, metadata in integrands_and_cds:
                        integral = Integral(integrand, integral_type, domain,
                                            subdomain_id, metadata, None)
                        integral = attach_coordinate_derivatives(integral, samecd_integrals[0])
                        integrals.append(integral)
    return Form(integrals)
Beispiel #41
0
    def expand(self):
        "Expand all members of the sum."

        # If sum is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # TODO: This function might need some optimisation.

        # Sort variables into symbols, products and fractions (add floats
        # directly to new list, will be handled later). Add fractions if
        # possible else add to list.
        new_variables = []
        syms = []
        prods = []
        frac_groups = {}
        # TODO: Rather than using '+', would it be more efficient to collect
        # the terms first?
        for var in self.vrs:
            exp = var.expand()
            # TODO: Should we also group fractions, or put this in a separate function?
            if exp._prec in (0, 4): # float or frac
                new_variables.append(exp)
            elif exp._prec == 1: # sym
                syms.append(exp)
            elif exp._prec == 2: # prod
                prods.append(exp)
            elif exp._prec == 3: # sum
                for v in exp.vrs:
                    if v._prec in (0, 4): # float or frac
                        new_variables.append(v)
                    elif v._prec == 1: # sym
                        syms.append(v)
                    elif v._prec == 2: # prod
                        prods.append(v)

        # Sort all variables in groups: [2*x, -7*x], [(x + y), (2*x + 4*y)] etc.
        # First handle product in order to add symbols if possible.
        prod_groups = {}
        for v in prods:
            if v.get_vrs() in prod_groups:
                prod_groups[v.get_vrs()] += v
            else:
                prod_groups[v.get_vrs()] = v

        sym_groups = {}
        # Loop symbols and add to appropriate groups.
        for v in syms:
            # First try to add to a product group.
            if (v,) in prod_groups:
                prod_groups[(v,)] += v
            # Then to other symbols.
            elif v in sym_groups:
                sym_groups[v] += v
            # Create a new entry in the symbols group.
            else:
                sym_groups[v] = v

        # Loop groups and add to new variable list.
        for k,v in sorted_by_key(sym_groups):
            new_variables.append(v)
        for k,v in sorted_by_key(prod_groups):
            new_variables.append(v)
#        for k,v in frac_groups.iteritems():
#            new_variables.append(v)
#            append(v)

        if len(new_variables) > 1:
            # Return new sum (will remove multiple instances of floats during construction).
            self._expanded = create_sum(sorted(new_variables))
            return self._expanded
        elif new_variables:
            # If we just have one variable left, return it since it is already expanded.
            self._expanded = new_variables[0]
            return self._expanded
        error("Where did the variables go?")
Beispiel #42
0
def group_form_integrals(form, domains, do_append_everywhere_integrals=True):
    """Group integrals by domain and type, performing canonical simplification.

    :arg form: the :class:`~.Form` to group the integrals of.
    :arg domains: an iterable of :class:`~.Domain`s.
    :returns: A new :class:`~.Form` with gathered integrands.
    """
    # Group integrals by domain and type
    integrals_by_domain_and_type = \
        group_integrals_by_domain_and_type(form.integrals(), domains)

    integrals = []
    for domain in domains:
        for integral_type in ufl.measure.integral_types():
            # Get integrals with this domain and type
            ddt_integrals = integrals_by_domain_and_type.get(
                (domain, integral_type))
            if ddt_integrals is None:
                continue

            # Group integrals by subdomain id, after splitting e.g.
            #   f*dx((1,2)) + g*dx((2,3)) -> f*dx(1) + (f+g)*dx(2) + g*dx(3)
            # (note: before this call, 'everywhere' is a valid subdomain_id,
            # and after this call, 'otherwise' is a valid subdomain_id)
            single_subdomain_integrals = \
                rearrange_integrals_by_single_subdomains(ddt_integrals, do_append_everywhere_integrals)

            for subdomain_id, ss_integrals in sorted_by_key(
                    single_subdomain_integrals):

                # strip the coordinate derivatives from all integrals
                # this yields a list of the form [(coordinate derivative, integral), ...]
                stripped_integrals_and_coordderivs = strip_coordinate_derivatives(
                    ss_integrals)

                # now group the integrals by the coordinate derivative
                def calc_hash(cd):
                    return sum(
                        sum(tuple_elem._ufl_compute_hash_()
                            for tuple_elem in tuple_) for tuple_ in cd)

                coordderiv_integrals_dict = {}
                for integral, coordderiv in stripped_integrals_and_coordderivs:
                    coordderivhash = calc_hash(coordderiv)
                    if coordderivhash in coordderiv_integrals_dict:
                        coordderiv_integrals_dict[coordderivhash][1].append(
                            integral)
                    else:
                        coordderiv_integrals_dict[coordderivhash] = (
                            coordderiv, [integral])

                # cd_integrals_dict is now a dict of the form
                # { hash: (CoordinateDerivative, [integral, integral, ...]), ... }
                # we can now put the integrals back together and then afterwards
                # apply the CoordinateDerivative again

                for cdhash, samecd_integrals in sorted_by_key(
                        coordderiv_integrals_dict):

                    # Accumulate integrands of integrals that share the
                    # same compiler data
                    integrands_and_cds = \
                        accumulate_integrands_with_same_metadata(samecd_integrals[1])

                    for integrand, metadata in integrands_and_cds:
                        integral = Integral(integrand, integral_type, domain,
                                            subdomain_id, metadata, None)
                        integral = attach_coordinate_derivatives(
                            integral, samecd_integrals[0])
                        integrals.append(integral)
    return Form(integrals)
Beispiel #43
0
    def sum(self, o, *operands):
        #print("Visiting Sum: " + "\noperands: \n" + "\n".join(map(repr, operands)))

        # Prefetch formats to speed up code generation.
        f_group = format["grouping"]
        f_add = format["add"]
        f_mult = format["multiply"]
        f_float = format["floating point"]
        code = {}

        # Loop operands that has to be summed and sort according to map (j,k).
        for op in operands:
            # If entries does already exist we can add the code, otherwise just
            # dump them in the element tensor.
            for key, val in sorted_by_key(op):
                if key in code:
                    code[key].append(val)
                else:
                    code[key] = [val]

        # Add sums and group if necessary.
        for key, val in sorted_by_key(code):

            # Exclude all zero valued terms from sum
            value = [v for v in val if not v is None]

            if len(value) > 1:
                # NOTE: Since we no longer call expand_indices, the following
                # is needed to prevent the code from exploding for forms like
                # HyperElasticity
                duplications = {}
                for val in value:
                    if val in duplications:
                        duplications[val] += 1
                        continue
                    duplications[val] = 1

                # Add a product for each term that has duplicate code
                expressions = []
                for expr, num_occur in sorted_by_key(duplications):
                    if num_occur > 1:
                        # Pre-multiply expression with number of occurrences
                        expressions.append(f_mult([f_float(num_occur), expr]))
                        continue
                    # Just add expression if there is only one
                    expressions.append(expr)
                ffc_assert(expressions, "Where did the expressions go?")

                if len(expressions) > 1:
                    code[key] = f_group(f_add(expressions))
                    continue
                code[key] = expressions[0]
            else:
                # Check for zero valued sum and delete from code
                # This might result in returning an empty dict, but that should
                # be interpreted as zero by other handlers.
                if not value:
                    del code[key]
                    continue
                code[key] = value[0]

        return code
Beispiel #44
0
def interpret_ufl_namespace(namespace):
    "Takes a namespace dict from an executed ufl file and converts it to a FileData object."
    # Object to hold all returned data
    ufd = FileData()

    # Extract object names for Form, Coefficient and FiniteElementBase objects
    # The use of id(obj) as key in object_names is necessary
    # because we need to distinguish between instances,
    # and not just between objects with different values.
    for name, value in sorted_by_key(namespace):
        # Store objects by reserved name OR instance id
        reserved_names = ("unknown", )  # Currently only one reserved name
        if name in reserved_names:
            # Store objects with reserved names
            ufd.reserved_objects[name] = value
            # FIXME: Remove after FFC is updated to use reserved_objects:
            ufd.object_names[name] = value
            ufd.object_by_name[name] = value
        elif isinstance(
                value, (FiniteElementBase, Coefficient, Argument, Form, Expr)):
            # Store instance <-> name mappings for important objects
            # without a reserved name
            ufd.object_names[id(value)] = name
            ufd.object_by_name[name] = value

    # Get list of exported forms
    forms = namespace.get("forms")
    if forms is None:
        # Get forms from object_by_name, which has already mapped
        # tuple->Form where needed
        def get_form(name):
            form = ufd.object_by_name.get(name)
            return form if isinstance(form, Form) else None

        a_form = get_form("a")
        L_form = get_form("L")
        M_form = get_form("M")
        forms = [a_form, L_form, M_form]
        # Add forms F and J if not "a" and "L" are used
        if a_form is None or L_form is None:
            F_form = get_form("F")
            J_form = get_form("J")
            forms += [F_form, J_form]
        # Remove Nones
        forms = [f for f in forms if isinstance(f, Form)]
    ufd.forms = forms

    # Validate types
    if not isinstance(ufd.forms, (list, tuple)):
        error("Expecting 'forms' to be a list or tuple, not '%s'." %
              type(ufd.forms))
    if not all(isinstance(a, Form) for a in ufd.forms):
        error("Expecting 'forms' to be a list of Form instances.")

    # Get list of exported elements
    elements = namespace.get("elements")
    if elements is None:
        elements = [ufd.object_by_name.get(name) for name in ("element", )]
        elements = [e for e in elements if e is not None]
    ufd.elements = elements

    # Validate types
    if not isinstance(ufd.elements, (list, tuple)):
        error("Expecting 'elements' to be a list or tuple, not '%s'." %
              type(ufd.elements))
    if not all(isinstance(e, FiniteElementBase) for e in ufd.elements):
        error(
            "Expecting 'elements' to be a list of FiniteElementBase instances."
        )

    # Get list of exported coefficients
    # TODO: Temporarily letting 'coefficients' override 'functions',
    # but allow 'functions' for compatibility
    functions = namespace.get("functions", [])
    if functions:
        warning(
            "Deprecation warning: Rename 'functions' to 'coefficients' to export coefficients."
        )
    ufd.coefficients = namespace.get("coefficients", functions)

    # Validate types
    if not isinstance(ufd.coefficients, (list, tuple)):
        error("Expecting 'coefficients' to be a list or tuple, not '%s'." %
              type(ufd.coefficients))
    if not all(isinstance(e, Coefficient) for e in ufd.coefficients):
        error(
            "Expecting 'coefficients' to be a list of Coefficient instances.")

    # Get list of exported expressions
    ufd.expressions = namespace.get("expressions", [])

    # Validate types
    if not isinstance(ufd.expressions, (list, tuple)):
        error("Expecting 'expressions' to be a list or tuple, not '%s'." %
              type(ufd.expressions))
    if not all(isinstance(e, Expr) for e in ufd.expressions):
        error("Expecting 'expressions' to be a list of Expr instances.")

    # Return file data
    return ufd
Beispiel #45
0
def group_vars(expr, format):
    """Group variables in an expression, such that:
    "x + y + z + 2*y + 6*z" = "x + 3*y + 7*z"
    "x*x + x*x + 2*x + 3*x + 5" = "2.0*x*x + 5.0*x + 5"
    "x*y + y*x + 2*x*y + 3*x + 0*x + 5" = "5.0*x*y + 3.0*x + 5"
    "(y + z)*x + 5*(y + z)*x" = "6.0*(y + z)*x"
    "1/(x*x) + 2*1/(x*x) + std::sqrt(x) + 6*std::sqrt(x)" = "3*1/(x*x) + 7*std::sqrt(x)"
    """

    # Get formats
    format_float = format["floating point"]
    add = format["add"](["", ""])
    mult = format["multiply"](["", ""])

    new_prods = {}

    # Get list of products
    prods = split_expression(expr, format, add)

    # Loop products and collect factors
    for p in prods:
        # Get list of variables, and do a basic sort
        vrs = split_expression(p, format, mult)
        factor = 1
        new_var = []

        # Try to multiply factor with variable, else variable must be multiplied by factor later
        # If we don't have a variable, set factor to zero and break
        for v in vrs:
            if v:
                try:
                    f = float(v)
                    factor *= f
                except:
                    new_var.append(v)
            else:
                factor = 0
                break

        # Create new variable that must be multiplied with factor. Add this
        # variable to dictionary, if it already exists add factor to other factors
        new_var.sort()
        new_var = mult.join(new_var)
        if new_var in new_prods:
            new_prods[new_var] += factor
        else:
            new_prods[new_var] = factor

    # Reset products
    prods = []
    for prod, f in sorted_by_key(new_prods):
        # If we have a product append mult of both
        if prod:
            # If factor is 1.0 we don't need it
            if f == 1.0:
                prods.append(prod)
            else:
                prods.append(mult.join([format_float(f), prod]))
        # If we just have a factor
        elif f:
            prods.append(format_float(f))

    prods.sort()
    return add.join(prods)