예제 #1
0
    def product(self, o, ops):
        if len(ops) != 2:
            error("Expecting two operands.")

        # Get the simple cases out of the way
        if len(ops[0]) == 1:  # True scalar * something
            a, = ops[0]
            return [Product(a, b) for b in ops[1]]
        elif len(ops[1]) == 1:  # Something * true scalar
            b, = ops[1]
            return [Product(a, b) for a in ops[0]]

        # Neither of operands are true scalars, this is the tricky part
        o0, o1 = o.ufl_operands

        # Get shapes and index shapes
        fi = o.ufl_free_indices
        fi0 = o0.ufl_free_indices
        fi1 = o1.ufl_free_indices
        fid = o.ufl_index_dimensions
        fid0 = o0.ufl_index_dimensions
        fid1 = o1.ufl_index_dimensions

        # Need to map each return component to one component of o0 and one component of o1.
        indices = compute_indices(fid)

        # Compute which component of o0 is used in component (comp,ind) of o
        # Compute strides within free index spaces
        ist0 = shape_to_strides(fid0)
        ist1 = shape_to_strides(fid1)
        # Map o0 and o1 indices to o indices
        indmap0 = [fi.index(i) for i in fi0]
        indmap1 = [fi.index(i) for i in fi1]
        indks = [(flatten_multiindex([ind[i] for i in indmap0], ist0),
                  flatten_multiindex([ind[i] for i in indmap1], ist1))
                 for ind in indices]

        # Build products for scalar components
        results = [Product(ops[0][k0], ops[1][k1]) for k0, k1 in indks]
        return results
예제 #2
0
    def form_argument(self, v, i):
        "Create new symbols for expressions that represent new values."
        symmetry = v.ufl_element().symmetry()

        if symmetry:
            # Build symbols with symmetric components skipped
            symbols = []
            mapped_symbols = {}
            for c in compute_indices(v.ufl_shape):
                # Build mapped component mc with symmetries from element considered
                mc = symmetry.get(c, c)

                # Get existing symbol or create new and store with mapped component mc as key
                s = mapped_symbols.get(mc)
                if s is None:
                    s = self.new_symbol()
                    mapped_symbols[mc] = s
                symbols.append(s)
        else:
            n = self.V_sizes[i]
            symbols = self.new_symbols(n)

        return symbols
예제 #3
0
def dependency_sorting(deplist, rank):
    #print "deplist = ", deplist

    def split(deps, state):
        left = []
        todo = []
        for dep in deps:
            if dep - state:
                left.append(dep)
            else:
                todo.append(dep)
        return todo, left

    deplistlist = []
    state = set()
    left = deplist

    # --- Initialization time
    #state.remove("x")
    precompute, left = split(left, state)
    deplistlist.append(precompute)

    state.add("x")
    precompute_quad, left = split(left, state)
    deplistlist.append(precompute_quad)

    # Permutations of 0/1 dependence of arguments
    indices = compute_indices((2, ) * rank)
    for bfs in indices[1:]:  # skip (0,...,0), already handled that
        for i, bf in reversed(list(enumerate(bfs))):
            n = "v%d" % i
            if bf:
                if n in state:
                    state.remove(n)
            else:
                state.add(n)
        next, left = split(left, state)
        deplistlist.append(next)

    # --- Runtime
    state.add("c")
    state.add("w")

    state.remove("x")
    runtime, left = split(left, state)
    deplistlist.append(runtime)

    state.add("x")
    runtime_quad, left = split(left, state)
    deplistlist.append(runtime_quad)

    indices = compute_indices((2, ) * rank)
    for bfs in indices[1:]:  # skip (0,...,0), already handled that
        for i, bf in reversed(list(enumerate(bfs))):
            n = "v%d" % i
            if bf:
                state.add(n)
            else:
                if n in state:
                    state.remove(n)
        next, left = split(left, state)
        deplistlist.append(next)

    ufl_assert(not left, "Shouldn't have anything left!")

    #print
    #print "Created deplistlist:"
    #for deps in deplistlist:
    #    print
    #    print "--- New stage:"
    #    print "\n".join(map(str, deps))
    #print

    return deplistlist
예제 #4
0
def dependency_sorting(deplist, rank):

    def split(deps, state):
        left = []
        todo = []
        for dep in deps:
            if dep - state:
                left.append(dep)
            else:
                todo.append(dep)
        return todo, left

    deplistlist = []
    state = set()
    left = deplist

    # --- Initialization time
    precompute, left = split(left, state)
    deplistlist.append(precompute)

    state.add("x")
    precompute_quad, left = split(left, state)
    deplistlist.append(precompute_quad)

    # Permutations of 0/1 dependence of arguments
    indices = compute_indices((2,)*rank)
    for bfs in indices[1:]:  # skip (0,...,0), already handled that
        for i, bf in reversed(enumerate(bfs)):
            n = "v%d" % i
            if bf:
                if n in state:
                    state.remove(n)
            else:
                state.add(n)
        next, left = split(left, state)
        deplistlist.append(next)

    # --- Runtime
    state.add("c")
    state.add("w")

    state.remove("x")
    runtime, left = split(left, state)
    deplistlist.append(runtime)

    state.add("x")
    runtime_quad, left = split(left, state)
    deplistlist.append(runtime_quad)

    indices = compute_indices((2,)*rank)
    for bfs in indices[1:]:  # skip (0,...,0), already handled that
        for i, bf in reversed(enumerate(bfs)):
            n = "v%d" % i
            if bf:
                state.add(n)
            else:
                if n in state:
                    state.remove(n)
        next, left = split(left, state)
        deplistlist.append(next)

    if left:
        error("Shouldn't have anything left!")

    return deplistlist
예제 #5
0
def map_component_tensor_arg_components(tensor):
    """Build a map from flattened components to subexpression.

    Builds integer list mapping between flattended components
    of tensor and its underlying indexed subexpression."""

    assert isinstance(tensor, ComponentTensor)

    # AKA tensor = as_tensor(indexed, multiindex)
    indexed, multiindex = tensor.ufl_operands

    e1 = indexed
    e2 = tensor  # e2 = as_tensor(e1, multiindex)
    mi = [i for i in multiindex if isinstance(i, Index)]

    # Get tensor and index shapes
    sh1 = e1.ufl_shape  # (sh)ape of e1
    sh2 = e2.ufl_shape  # (sh)ape of e2
    fi1 = e1.ufl_free_indices  # (f)ree (i)ndices of e1
    fi2 = e2.ufl_free_indices  # ...
    fid1 = e1.ufl_index_dimensions  # (f)ree (i)ndex (d)imensions of e1
    fid2 = e2.ufl_index_dimensions  # ...

    # Compute total shape (tsh) of e1 and e2
    tsh1 = sh1 + fid1
    tsh2 = sh2 + fid2
    r1 = len(tsh1)  # 'total rank' or e1
    r2 = len(tsh2)  # ...
    str1 = shape_to_strides(tsh1)
    assert not sh1
    assert sh2
    assert len(mi) == len(multiindex)
    assert ufl.product(tsh1) == ufl.product(tsh2)
    assert fi1

    assert all(i in fi1 for i in fi2)

    nmui = len(multiindex)
    assert nmui == len(sh2)

    # Build map from fi2/fid2 position (-offset nmui) to fi1/fid1 position
    p2_to_p1_map = [None] * r2
    for k, i in enumerate(fi2):
        p2_to_p1_map[k + nmui] = fi1.index(i)

    # Build map from fi1/fid1 position to mi position
    for k, i in enumerate(mi):
        p2_to_p1_map[k] = fi1.index(mi[k].count())

    # Build map from flattened e1 component to flattened e2 component
    perm2 = compute_indices(tsh2)
    ni2 = ufl.product(tsh2)

    # Situation: e2 = as_tensor(e1, mi)
    d2 = [None] * ni2
    p1 = [None] * r1
    for c2, p2 in enumerate(perm2):
        for k2, k1 in enumerate(p2_to_p1_map):
            p1[k1] = p2[k2]
        c1 = flatten_multiindex(p1, str1)
        d2[c2] = c1

    # Consistency checks
    assert all(isinstance(x, int) for x in d2)
    assert len(set(d2)) == len(d2)
    return d2
예제 #6
0
파일: mixedelement.py 프로젝트: FEniCS/ufl
    def __init__(self, family, cell=None, degree=None, shape=None,
                 symmetry=None, quad_scheme=None):
        """Create tensor element (repeated mixed element with optional symmetries).

        :arg family: The family string, or an existing FiniteElement.
        :arg cell: The geometric cell (ignored if family is a FiniteElement).
        :arg degree: The polynomial degree (ignored if family is a FiniteElement).
        :arg shape: The shape of the element (defaults to a square
             tensor given by the geometric dimension of the cell).
        :arg symmetry: Optional symmetries.
        :arg quad_scheme: Optional quadrature scheme (ignored if
             family is a FiniteElement)."""

        if isinstance(family, FiniteElementBase):
            sub_element = family
            cell = sub_element.cell()
        else:
            if cell is not None:
                cell = as_cell(cell)
            # Create scalar sub element
            sub_element = FiniteElement(family, cell, degree, quad_scheme=quad_scheme)

        if sub_element.value_shape() != ():
            error("Expecting only scalar valued subelement for TensorElement.")

        # Set default shape if not specified
        if shape is None:
            if cell is None:
                error("Cannot infer tensor shape without a cell.")
            dim = cell.geometric_dimension()
            shape = (dim, dim)

        if symmetry is None:
            symmetry = EmptyDict
        elif symmetry is True:
            # Construct default symmetry dict for matrix elements
            if not (len(shape) == 2 and shape[0] == shape[1]):
                error("Cannot set automatic symmetry for non-square tensor.")
            symmetry = dict(((i, j), (j, i)) for i in range(shape[0])
                            for j in range(shape[1]) if i > j)
        else:
            if not isinstance(symmetry, dict):
                error("Expecting symmetry to be None (unset), True, or dict.")

        # Validate indices in symmetry dict
        for i, j in symmetry.items():
            if len(i) != len(j):
                error("Non-matching length of symmetry index tuples.")
            for k in range(len(i)):
                if not (i[k] >= 0 and j[k] >= 0 and i[k] < shape[k] and j[k] < shape[k]):
                    error("Symmetry dimensions out of bounds.")

        # Compute all index combinations for given shape
        indices = compute_indices(shape)

        # Compute mapping from indices to sub element number,
        # accounting for symmetry
        sub_elements = []
        sub_element_mapping = {}
        for index in indices:
            if index in symmetry:
                continue
            sub_element_mapping[index] = len(sub_elements)
            sub_elements += [sub_element]

        # Update mapping for symmetry
        for index in indices:
            if index in symmetry:
                sub_element_mapping[index] = sub_element_mapping[symmetry[index]]
        flattened_sub_element_mapping = [sub_element_mapping[index] for i,
                                         index in enumerate(indices)]

        # Compute value shape
        value_shape = shape

        # Compute reference value shape based on symmetries
        if symmetry:
            # Flatten and subtract symmetries
            reference_value_shape = (product(shape)-len(symmetry),)
            self._mapping = "symmetries"
        else:
            # Do not flatten if there are no symmetries
            reference_value_shape = shape
            self._mapping = "identity"

        # Initialize element data
        MixedElement.__init__(self, sub_elements, value_shape=value_shape,
                              reference_value_shape=reference_value_shape)
        self._family = sub_element.family()
        self._degree = sub_element.degree()
        self._sub_element = sub_element
        self._shape = shape
        self._symmetry = symmetry
        self._sub_element_mapping = sub_element_mapping
        self._flattened_sub_element_mapping = flattened_sub_element_mapping

        # Cache repr string
        self._repr = "TensorElement(%s, shape=%s, symmetry=%s)" % (
            repr(sub_element), repr(self._shape), repr(self._symmetry))
예제 #7
0
    def __init__(self,
                 family,
                 cell=None,
                 degree=None,
                 shape=None,
                 symmetry=None,
                 quad_scheme=None):
        """Create tensor element (repeated mixed element with optional symmetries).

        :arg family: The family string, or an existing FiniteElement.
        :arg cell: The geometric cell (ignored if family is a FiniteElement).
        :arg degree: The polynomial degree (ignored if family is a FiniteElement).
        :arg shape: The shape of the element (defaults to a square
             tensor given by the geometric dimension of the cell).
        :arg symmetry: Optional symmetries.
        :arg quad_scheme: Optional quadrature scheme (ignored if
             family is a FiniteElement)."""

        if isinstance(family, FiniteElementBase):
            sub_element = family
            cell = sub_element.cell()
        else:
            if cell is not None:
                cell = as_cell(cell)
            # Create scalar sub element
            sub_element = FiniteElement(family,
                                        cell,
                                        degree,
                                        quad_scheme=quad_scheme)

        # Set default shape if not specified
        if shape is None:
            if cell is None:
                error("Cannot infer tensor shape without a cell.")
            dim = cell.geometric_dimension()
            shape = (dim, dim)

        if symmetry is None:
            symmetry = EmptyDict
        elif symmetry is True:
            # Construct default symmetry dict for matrix elements
            if not (len(shape) == 2 and shape[0] == shape[1]):
                error("Cannot set automatic symmetry for non-square tensor.")
            symmetry = dict(((i, j), (j, i)) for i in range(shape[0])
                            for j in range(shape[1]) if i > j)
        else:
            if not isinstance(symmetry, dict):
                error("Expecting symmetry to be None (unset), True, or dict.")

        # Validate indices in symmetry dict
        for i, j in symmetry.items():
            if len(i) != len(j):
                error("Non-matching length of symmetry index tuples.")
            for k in range(len(i)):
                if not (i[k] >= 0 and j[k] >= 0 and i[k] < shape[k]
                        and j[k] < shape[k]):
                    error("Symmetry dimensions out of bounds.")

        # Compute all index combinations for given shape
        indices = compute_indices(shape)

        # Compute mapping from indices to sub element number,
        # accounting for symmetry
        sub_elements = []
        sub_element_mapping = {}
        for index in indices:
            if index in symmetry:
                continue
            sub_element_mapping[index] = len(sub_elements)
            sub_elements += [sub_element]

        # Update mapping for symmetry
        for index in indices:
            if index in symmetry:
                sub_element_mapping[index] = sub_element_mapping[
                    symmetry[index]]
        flattened_sub_element_mapping = [
            sub_element_mapping[index] for i, index in enumerate(indices)
        ]

        # Compute value shape
        value_shape = shape

        # Compute reference value shape based on symmetries
        if symmetry:
            # Flatten and subtract symmetries
            reference_value_shape = (product(shape) - len(symmetry), )
            self._mapping = "symmetries"
        else:
            # Do not flatten if there are no symmetries
            reference_value_shape = shape
            self._mapping = "identity"

        value_shape = value_shape + sub_element.value_shape()
        reference_value_shape = reference_value_shape + sub_element.reference_value_shape(
        )
        # Initialize element data
        MixedElement.__init__(self,
                              sub_elements,
                              value_shape=value_shape,
                              reference_value_shape=reference_value_shape)
        self._family = sub_element.family()
        self._degree = sub_element.degree()
        self._sub_element = sub_element
        self._shape = shape
        self._symmetry = symmetry
        self._sub_element_mapping = sub_element_mapping
        self._flattened_sub_element_mapping = flattened_sub_element_mapping

        # Cache repr string
        self._repr = "TensorElement(%s, shape=%s, symmetry=%s)" % (
            repr(sub_element), repr(self._shape), repr(self._symmetry))
예제 #8
0
def map_indexed_arg_components(indexed):
    """Build a map from flattened components to subexpression.

    Builds integer list mapping between flattened components
    of indexed expression and its underlying tensor-valued subexpression."""

    assert isinstance(indexed, Indexed)

    # AKA indexed = tensor[multiindex]
    tensor, multiindex = indexed.ufl_operands

    # AKA e1 = e2[multiindex]
    # (this renaming is historical, but kept for consistency with all the variables *1,*2 below)
    e2 = tensor
    e1 = indexed

    # Get tensor and index shape
    sh1 = e1.ufl_shape
    sh2 = e2.ufl_shape
    fi1 = e1.ufl_free_indices
    fi2 = e2.ufl_free_indices
    fid1 = e1.ufl_index_dimensions
    fid2 = e2.ufl_index_dimensions

    # Compute regular and total shape
    tsh1 = sh1 + fid1
    tsh2 = sh2 + fid2
    # r1 = len(tsh1)
    r2 = len(tsh2)
    # str1 = shape_to_strides(tsh1)
    str2 = shape_to_strides(tsh2)
    assert not sh1
    assert sh2  # Must have shape to be indexed in the first place
    assert ufl.product(tsh1) <= ufl.product(tsh2)

    # Build map from fi2/fid2 position (-offset nmui) to fi1/fid1 position
    ind2_to_ind1_map = [None] * len(fi2)
    for k, i in enumerate(fi2):
        ind2_to_ind1_map[k] = fi1.index(i)

    # Build map from fi1/fid1 position to mi position
    nmui = len(multiindex)
    multiindex_to_ind1_map = [None] * nmui
    for k, i in enumerate(multiindex):
        if isinstance(i, Index):
            multiindex_to_ind1_map[k] = fi1.index(i.count())

    # Build map from flattened e1 component to flattened e2 component
    perm1 = compute_indices(tsh1)
    ni1 = ufl.product(tsh1)

    # Situation: e1 = e2[mi]
    d1 = [None] * ni1
    p2 = [None] * r2
    assert len(sh2) == nmui
    for k, i in enumerate(multiindex):
        if isinstance(i, FixedIndex):
            p2[k] = int(i)
    for c1, p1 in enumerate(perm1):
        for k, i in enumerate(multiindex):
            if isinstance(i, Index):
                p2[k] = p1[multiindex_to_ind1_map[k]]
        for k, i in enumerate(ind2_to_ind1_map):
            p2[nmui + k] = p1[i]
        c2 = flatten_multiindex(p2, str2)
        d1[c1] = c2

    # Consistency checks
    assert all(isinstance(x, int) for x in d1)
    assert len(set(d1)) == len(d1)
    return d1
예제 #9
0
def rebuild_with_scalar_subexpressions(G, targets=None):
    """Build a new expression2index mapping where each subexpression is scalar valued.

    Input:
    - G.e2i
    - G.V
    - G.V_symbols
    - G.total_unique_symbols

    Output:
    - NV   - Array with reverse mapping from index to expression
    - nvs  - Tuple of ne2i indices corresponding to the last vertex of G.V

    Old output now no longer returned but possible to restore if needed:
    - ne2i - Mapping from scalar subexpressions to a contiguous unique index
    - W    - Array with reconstructed scalar subexpressions for each original symbol
    """

    # From simplefsi3d.ufl:
    # GRAPH SIZE: len(G.V), G.total_unique_symbols
    # GRAPH SIZE: 16251   635272
    # GRAPH SIZE:   473     8210
    # GRAPH SIZE:  9663   238021
    # GRAPH SIZE: 88913  3448634  #  3.5 M!!!

    # Algorithm to apply to each subexpression
    reconstruct_scalar_subexpressions = ReconstructScalarSubexpressions()

    # Array to store the scalar subexpression in for each symbol
    W = numpy.empty(G.total_unique_symbols, dtype=object)

    # Iterate over each graph node in order
    for i, v in enumerate(G.V):
        # Find symbols of v components
        vs = G.V_symbols[i]

        # Skip if there's nothing new here (should be the case for indexing types)
        if all(W[s] is not None for s in vs):
            continue

        if is_modified_terminal(v):
            # if v.ufl_free_indices:
            #     error("Expecting no free indices.")
            sh = v.ufl_shape
            if sh:
                # Store each terminal expression component.
                # We may not actually need all of these later,
                # but that will be optimized away.
                # Note: symmetries will be dealt with in the value numbering.
                ws = [v[c] for c in compute_indices(sh)]
            else:
                # Store single modified terminal expression component
                if len(vs) != 1:
                    error("Expecting single symbol for scalar valued modified terminal.")
                ws = [v]
            # FIXME: Replace ws[:] with 0's if its table is empty
            # Possible redesign: loop over modified terminals only first,
            # then build tables for them, set W[s] = 0.0 for modified terminals with zero table,
            # then loop over non-(modified terminal)s to reconstruct expression.
        else:
            # Find symbols of operands
            sops = []
            for j, vop in enumerate(v.ufl_operands):
                if isinstance(vop, MultiIndex):
                    # TODO: Store MultiIndex in G.V and allocate a symbol to it for this to work
                    if not isinstance(v, IndexSum):
                        error("Not expecting a %s." % type(v))
                    sops.append(())
                else:
                    # TODO: Build edge datastructure and use instead?
                    # k = G.E[i][j]
                    k = G.e2i[vop]
                    sops.append(G.V_symbols[k])

            # Fetch reconstructed operand expressions
            wops = [tuple(W[k] for k in so) for so in sops]

            # Reconstruct scalar subexpressions of v
            ws = reconstruct_scalar_subexpressions(v, wops)

            # Store all scalar subexpressions for v symbols
            if len(vs) != len(ws):
                error("Expecting one symbol for each expression.")

        # Store each new scalar subexpression in W at the index of its symbol
        handled = set()
        for s, w in zip(vs, ws):
            if W[s] is None:
                W[s] = w
                handled.add(s)
            else:
                assert s in handled  # Result of symmetry!

    # Find symbols of requested targets or final v from input graph
    if targets is None:
        targets = [G.V[-1]]

    # Attempt to extend this to multiple target expressions
    scalar_target_expressions = []
    for target in targets:
        ti = G.e2i[target]
        vs = G.V_symbols[ti]
        # Sanity check: assert that we've handled these symbols
        if any(W[s] is None for s in vs):
            error("Expecting that all symbols in vs are handled at this point.")
        scalar_target_expressions.append([W[s] for s in vs])

    # Return the scalar expressions for each of the components
    assert len(scalar_target_expressions) == 1  # TODO: Currently expected by callers, fix those first
    return scalar_target_expressions[0]  # ... TODO: then return list
예제 #10
0
def split(v):
    """UFL operator: If v is a Coefficient or Argument in a mixed space, returns
    a tuple with the function components corresponding to the subelements."""

    # Default range is all of v
    begin = 0
    end = None

    if isinstance(v, Indexed):
        # Special case: split previous output of split again
        # Consistent with simple element, just return function in a tuple
        return (v, )

    elif isinstance(v, ListTensor):
        # Special case: split previous output of split again
        ops = v.ufl_operands
        if all(isinstance(comp, Indexed) for comp in ops):
            args = [comp.ufl_operands[0] for comp in ops]
            if all(args[0] == args[i] for i in range(1, len(args))):
                # Get innermost terminal here and its element
                v = args[0]
                # Get relevant range of v components
                begin, = ops[0].ufl_operands[1]
                end, = ops[-1].ufl_operands[1]
                begin = int(begin)
                end = int(end) + 1
            else:
                error("Don't know how to split %s." % (v, ))
        else:
            error("Don't know how to split %s." % (v, ))

    # Special case: simple element, just return function in a tuple
    element = v.ufl_element()
    if not isinstance(element, MixedElement):
        assert end is None
        return (v, )

    if isinstance(element, TensorElement):
        if element.symmetry():
            error("Split not implemented for symmetric tensor elements.")

    if len(v.ufl_shape) != 1:
        error(
            "Don't know how to split tensor valued mixed functions without flattened index space."
        )

    # Compute value size and set default range end
    value_size = product(element.value_shape())
    if end is None:
        end = value_size
    else:
        # Recursively dive into mixedelement in to subelement
        # corresponding to beginning of range
        j = begin
        while True:
            sub_i, j = element.extract_subelement_component(j)
            element = element.sub_elements()[sub_i]
            # Then break when we find the subelement that covers the whole range
            if product(element.value_shape()) == (end - begin):
                break

    # Build expressions representing the subfunction of v for each subelement
    offset = begin
    sub_functions = []
    for i, e in enumerate(element.sub_elements()):
        # Get shape, size, indices, and v components
        # corresponding to subelement value
        shape = e.value_shape()
        strides = shape_to_strides(shape)
        rank = len(shape)
        sub_size = product(shape)
        subindices = [
            flatten_multiindex(c, strides) for c in compute_indices(shape)
        ]
        components = [v[k + offset] for k in subindices]

        # Shape components into same shape as subelement
        if rank == 0:
            subv, = components
        elif rank <= 1:
            subv = as_vector(components)
        elif rank == 2:
            subv = as_matrix([
                components[i * shape[1]:(i + 1) * shape[1]]
                for i in range(shape[0])
            ])
        else:
            error(
                "Don't know how to split functions with sub functions of rank %d."
                % rank)

        offset += sub_size
        sub_functions.append(subv)

    if end != offset:
        error(
            "Function splitting failed to extract components for whole intended range. Something is wrong."
        )

    return tuple(sub_functions)
예제 #11
0
def split(v):
    """UFL operator: If v is a Coefficient or Argument in a mixed space, returns
    a tuple with the function components corresponding to the subelements."""

    # Default range is all of v
    begin = 0
    end = None

    if isinstance(v, Indexed):
        # Special case: split previous output of split again
        # Consistent with simple element, just return function in a tuple
        return (v,)

    elif isinstance(v, ListTensor):
        # Special case: split previous output of split again
        ops = v.ufl_operands
        if all(isinstance(comp, Indexed) for comp in ops):
            args = [comp.ufl_operands[0] for comp in ops]
            if all(args[0] == args[i] for i in range(1, len(args))):
                # Get innermost terminal here and its element
                v = args[0]
                # Get relevant range of v components
                begin, = ops[0].ufl_operands[1]
                end, = ops[-1].ufl_operands[1]
                begin = int(begin)
                end = int(end) + 1
            else:
                error("Don't know how to split %s." % (v,))
        else:
            error("Don't know how to split %s." % (v,))

    # Special case: simple element, just return function in a tuple
    element = v.ufl_element()
    if not isinstance(element, MixedElement):
        assert end is None
        return (v,)

    if isinstance(element, TensorElement):
        if element.symmetry():
            error("Split not implemented for symmetric tensor elements.")

    if len(v.ufl_shape) != 1:
        error("Don't know how to split tensor valued mixed functions without flattened index space.")

    # Compute value size and set default range end
    value_size = product(element.value_shape())
    if end is None:
        end = value_size
    else:
        # Recursively dive into mixedelement in to subelement
        # corresponding to beginning of range
        j = begin
        while True:
            sub_i, j = element.extract_subelement_component(j)
            element = element.sub_elements()[sub_i]
            # Then break when we find the subelement that covers the whole range
            if product(element.value_shape()) == (end - begin):
                break

    # Build expressions representing the subfunction of v for each subelement
    offset = begin
    sub_functions = []
    for i, e in enumerate(element.sub_elements()):
        # Get shape, size, indices, and v components
        # corresponding to subelement value
        shape = e.value_shape()
        strides = shape_to_strides(shape)
        rank = len(shape)
        sub_size = product(shape)
        subindices = [flatten_multiindex(c, strides)
                      for c in compute_indices(shape)]
        components = [v[k + offset] for k in subindices]

        # Shape components into same shape as subelement
        if rank == 0:
            subv, = components
        elif rank <= 1:
            subv = as_vector(components)
        elif rank == 2:
            subv = as_matrix([components[i*shape[1]: (i+1)*shape[1]]
                              for i in range(shape[0])])
        else:
            error("Don't know how to split functions with sub functions of rank %d." % rank)

        offset += sub_size
        sub_functions.append(subv)

    if end != offset:
        error("Function splitting failed to extract components for whole intended range. Something is wrong.")

    return tuple(sub_functions)
예제 #12
0
    def _modified_terminal(self, v, i):
        """Modifiers:
        terminal           - the underlying Terminal object
        global_derivatives - tuple of ints, each meaning derivative in that global direction
        local_derivatives  - tuple of ints, each meaning derivative in that local direction
        reference_value    - bool, whether this is represented in reference frame
        averaged           - None, 'facet' or 'cell'
        restriction        - None, '+' or '-'
        component          - tuple of ints, the global component of the Terminal
        flat_component     - single int, flattened local component of the Terminal, considering symmetry
        """
        # (1) mt.terminal.ufl_shape defines a core indexing space UNLESS mt.reference_value,
        #     in which case the reference value shape of the element must be used.
        # (2) mt.terminal.ufl_element().symmetry() defines core symmetries
        # (3) averaging and restrictions define distinct symbols, no additional symmetries
        # (4) two or more grad/reference_grad defines distinct symbols with additional symmetries

        # v is not necessary scalar here, indexing in (0,...,0) picks the first scalar component
        # to analyse, which should be sufficient to get the base shape and derivatives
        if v.ufl_shape:
            mt = analyse_modified_terminal(v[(0,) * len(v.ufl_shape)])
        else:
            mt = analyse_modified_terminal(v)

        # Get derivatives
        num_ld = len(mt.local_derivatives)
        num_gd = len(mt.global_derivatives)
        assert not (num_ld and num_gd)
        if num_ld:
            domain = mt.terminal.ufl_domain()
            tdim = domain.topological_dimension()
            d_components = compute_indices((tdim,) * num_ld)
        elif num_gd:
            domain = mt.terminal.ufl_domain()
            gdim = domain.geometric_dimension()
            d_components = compute_indices((gdim,) * num_gd)
        else:
            d_components = [()]

        # Get base shape without the derivative axes
        base_components = compute_indices(mt.base_shape)

        # Build symbols with symmetric components and derivatives skipped
        symbols = []
        mapped_symbols = {}
        for bc in base_components:
            for dc in d_components:
                # Build mapped component mc with symmetries from element and derivatives combined
                mbc = mt.base_symmetry.get(bc, bc)
                mdc = tuple(sorted(dc))
                mc = mbc + mdc

                # Get existing symbol or create new and store with mapped component mc as key
                s = mapped_symbols.get(mc)
                if s is None:
                    s = self.new_symbol()
                    mapped_symbols[mc] = s
                symbols.append(s)

        # Consistency check before returning symbols
        assert not v.ufl_free_indices
        if product(v.ufl_shape) != len(symbols):
            error("Internal error in value numbering.")
        return symbols
예제 #13
0
    def _mapped(self, t):
        # Check that we have a valid input object
        if not isinstance(t, Terminal):
            error("Expecting a Terminal.")

        # Get modifiers accumulated by previous handler calls
        ngrads = self._ngrads
        restricted = self._restricted
        avg = self._avg
        if avg != "":
            error("Averaging not implemented.")  # FIXME

        # These are the global (g) and reference (r) values
        if isinstance(t, FormArgument):
            g = t
            r = ReferenceValue(g)
        elif isinstance(t, GeometricQuantity):
            g = t
            r = g
        else:
            error("Unexpected type {0}.".format(type(t).__name__))

        # Some geometry mapping objects we may need multiple times below
        domain = t.ufl_domain()
        J = Jacobian(domain)
        detJ = JacobianDeterminant(domain)
        K = JacobianInverse(domain)

        # Restrict geometry objects if applicable
        if restricted:
            J = J(restricted)
            detJ = detJ(restricted)
            K = K(restricted)

        # Create Hdiv mapping from possibly restricted geometry objects
        Mdiv = (1.0 / detJ) * J

        # Get component indices of global and reference terminal objects
        gtsh = g.ufl_shape
        # rtsh = r.ufl_shape
        gtcomponents = compute_indices(gtsh)
        # rtcomponents = compute_indices(rtsh)

        # Create core modified terminal, with eventual
        # layers of grad applied directly to the terminal,
        # then eventual restriction applied last
        for i in range(ngrads):
            g = Grad(g)
            r = ReferenceGrad(r)
        if restricted:
            g = g(restricted)
            r = r(restricted)

        # Get component indices of global and reference objects with
        # grads applied
        gsh = g.ufl_shape
        # rsh = r.ufl_shape
        # gcomponents = compute_indices(gsh)
        # rcomponents = compute_indices(rsh)

        # Get derivative component indices
        dsh = gsh[len(gtsh):]
        dcomponents = compute_indices(dsh)

        # Create nested array to hold expressions for global
        # components mapped from reference values
        def ndarray(shape):
            if len(shape) == 0:
                return [None]
            elif len(shape) == 1:
                return [None] * shape[-1]
            else:
                return [ndarray(shape[1:]) for i in range(shape[0])]
        global_components = ndarray(gsh)

        # Compute mapping from reference values for each global component
        for gtc in gtcomponents:

            if isinstance(t, FormArgument):

                # Find basic subelement and element-local component
                # ec, element, eoffset = t.ufl_element().extract_component2(gtc) # FIXME: Translate this correctly
                eoffset = 0
                ec, element = t.ufl_element().extract_reference_component(gtc)

                # Select mapping M from element, pick row emapping =
                # M[ec,:], or emapping = [] if no mapping
                if isinstance(element, MixedElement):
                    error("Expecting a basic element here.")
                mapping = element.mapping()
                if mapping == "contravariant Piola":  # S == HDiv:
                    # Handle HDiv elements with contravariant piola
                    # mapping contravariant_hdiv_mapping = (1/det J) *
                    # J * PullbackOf(o)
                    ec, = ec
                    emapping = Mdiv[ec, :]
                elif mapping == "covariant Piola":  # S == HCurl:
                    # Handle HCurl elements with covariant piola mapping
                    # covariant_hcurl_mapping = JinvT * PullbackOf(o)
                    ec, = ec
                    emapping = K[:, ec]  # Column of K is row of K.T
                elif mapping == "identity":
                    emapping = None
                else:
                    error("Unknown mapping {0}".format(mapping))

            elif isinstance(t, GeometricQuantity):
                eoffset = 0
                emapping = None

            else:
                error("Unexpected type {0}.".format(type(t).__name__))

            # Create indices
            # if rtsh:
            #     i = Index()
            if len(dsh) != ngrads:
                error("Mismatch between derivative shape and ngrads.")
            if ngrads:
                ii = indices(ngrads)
            else:
                ii = ()

            # Apply mapping row to reference object
            if emapping:  # Mapped, always nonscalar terminal Not
                # using IndexSum for the mapping row dot product to
                # keep it simple, because we don't have a slice type
                emapped_ops = [emapping[s] * Indexed(r, MultiIndex((FixedIndex(eoffset + s),) + ii))
                               for s in range(len(emapping))]
                emapped = sum(emapped_ops[1:], emapped_ops[0])
            elif gtc:  # Nonscalar terminal, unmapped
                emapped = Indexed(r, MultiIndex((FixedIndex(eoffset),) + ii))
            elif ngrads:  # Scalar terminal, unmapped, with derivatives
                emapped = Indexed(r, MultiIndex(ii))
            else:  # Scalar terminal, unmapped, no derivatives
                emapped = r

            for di in dcomponents:
                # Multiply derivative mapping rows, parameterized by
                # free column indices
                dmapping = as_ufl(1)
                for j in range(ngrads):
                    dmapping *= K[ii[j], di[j]]  # Row of K is column of JinvT

                # Compute mapping from reference values for this
                # particular global component
                global_value = dmapping * emapped

                # Apply index sums
                # if rtsh:
                #     global_value = IndexSum(global_value, MultiIndex((i,)))
                # for j in range(ngrads): # Applied implicitly in the dmapping * emapped above
                #     global_value = IndexSum(global_value, MultiIndex((ii[j],)))

                # This is the component index into the full object
                # with grads applied
                gc = gtc + di

                # Insert in nested list
                comp = global_components
                for i in gc[:-1]:
                    comp = comp[i]
                comp[0 if gc == () else gc[-1]] = global_value

        # Wrap nested list in as_tensor unless we have a scalar
        # expression
        if gsh:
            tensor = as_tensor(global_components)
        else:
            tensor, = global_components
        return tensor
예제 #14
0
def dependency_sorting(deplist, rank):
    #print "deplist = ", deplist

    def split(deps, state):
        left = []
        todo = []
        for dep in deps:
            if dep - state:
                left.append(dep)
            else:
                todo.append(dep)
        return todo, left

    deplistlist = []
    state = set()
    left = deplist

    # --- Initialization time
    #state.remove("x")
    precompute, left = split(left, state)
    deplistlist.append(precompute)

    state.add("x")
    precompute_quad, left = split(left, state)
    deplistlist.append(precompute_quad)

    # Permutations of 0/1 dependence of arguments
    indices = compute_indices((2,)*rank)
    for bfs in indices[1:]: # skip (0,...,0), already handled that
        for i, bf in reversed(list(enumerate(bfs))):
            n = "v%d" % i
            if bf:
                if n in state:
                    state.remove(n)
            else:
                state.add(n)
        next, left = split(left, state)
        deplistlist.append(next)

    # --- Runtime
    state.add("c")
    state.add("w")

    state.remove("x")
    runtime, left = split(left, state)
    deplistlist.append(runtime)

    state.add("x")
    runtime_quad, left = split(left, state)
    deplistlist.append(runtime_quad)

    indices = compute_indices((2,)*rank)
    for bfs in indices[1:]: # skip (0,...,0), already handled that
        for i, bf in reversed(list(enumerate(bfs))):
            n = "v%d" % i
            if bf:
                state.add(n)
            else:
                if n in state:
                    state.remove(n)
        next, left = split(left, state)
        deplistlist.append(next)

    ufl_assert(not left, "Shouldn't have anything left!")

    #print
    #print "Created deplistlist:"
    #for deps in deplistlist:
    #    print
    #    print "--- New stage:"
    #    print "\n".join(map(str, deps))
    #print

    return deplistlist
예제 #15
0
    def __init__(self, family, domain, degree, shape=None,
                 symmetry=None, quad_scheme=None):
        "Create tensor element (repeated mixed element with optional symmetries)"
        if domain is not None:
            domain = as_domain(domain)

        # Set default shape if not specified
        if shape is None:
            ufl_assert(domain is not None,
                       "Cannot infer vector dimension without a domain.")
            dim = domain.geometric_dimension()
            shape = (dim, dim)

        # Construct default symmetry for matrix elements
        if symmetry == True:
            ufl_assert(len(shape) == 2 and shape[0] == shape[1],
                       "Cannot set automatic symmetry for non-square tensor.")
            symmetry = dict( ((i,j), (j,i)) for i in range(shape[0])
                             for j in range(shape[1]) if i > j )

        # Validate indices in symmetry dict
        if isinstance(symmetry, dict):
            for i,j in symmetry.iteritems():
                ufl_assert(len(i) == len(j),
                           "Non-matching length of symmetry index tuples.")
                for k in range(len(i)):
                    ufl_assert(i[k] >= 0 and j[k] >= 0 and
                               i[k] < shape[k] and j[k] < shape[k],
                               "Symmetry dimensions out of bounds.")
        else:
            ufl_assert(symmetry is None, "Expecting symmetry to be None, True, or dict.")

        # Compute all index combinations for given shape
        indices = compute_indices(shape)

        # Compute sub elements and mapping from indices
        # to sub elements, accounting for symmetry
        sub_element = FiniteElement(family, domain, degree, quad_scheme)
        sub_elements = []
        sub_element_mapping = {}
        for index in indices:
            if symmetry and index in symmetry:
                continue
            sub_element_mapping[index] = len(sub_elements)
            sub_elements += [sub_element]

        # Update mapping for symmetry
        for index in indices:
            if symmetry and index in symmetry:
                sub_element_mapping[index] = sub_element_mapping[symmetry[index]]

        # Get common family name (checked in FiniteElement.__init__)
        family = sub_element.family()

        # Compute value shape
        value_shape = shape + sub_element.value_shape()

        # Initialize element data
        super(TensorElement, self).__init__(sub_elements, value_shape=value_shape)
        self._family = family
        self._degree = degree
        self._sub_element = sub_element
        self._shape = shape
        self._symmetry = symmetry
        self._sub_element_mapping = sub_element_mapping

        # Cache repr string
        self._repr = "TensorElement(%r, %r, %r, %r, %r, %r)" % \
            (self._family, self._domain, self._degree, self._shape,
             self._symmetry, quad_scheme)
예제 #16
0
파일: indexing.py 프로젝트: FEniCS/ffc
def map_component_tensor_arg_components(tensor):
    """Build integer list mapping between flattended components
    of tensor and its underlying indexed subexpression."""

    assert isinstance(tensor, ComponentTensor)

    # AKA tensor = as_tensor(indexed, multiindex)
    indexed, multiindex = tensor.ufl_operands

    e1 = indexed
    e2 = tensor  # e2 = as_tensor(e1, multiindex)
    mi = [i for i in multiindex if isinstance(i, Index)]

    # Get tensor and index shapes
    sh1 = e1.ufl_shape  # (sh)ape of e1
    sh2 = e2.ufl_shape  # (sh)ape of e2
    fi1 = e1.ufl_free_indices  # (f)ree (i)ndices of e1
    fi2 = e2.ufl_free_indices  # ...
    fid1 = e1.ufl_index_dimensions  # (f)ree (i)ndex (d)imensions of e1
    fid2 = e2.ufl_index_dimensions  # ...

    # Compute total shape (tsh) of e1 and e2
    tsh1 = sh1 + fid1
    tsh2 = sh2 + fid2
    r1 = len(tsh1)  # 'total rank' or e1
    r2 = len(tsh2)  # ...
    str1 = shape_to_strides(tsh1)
    assert not sh1
    assert sh2
    assert len(mi) == len(multiindex)
    assert product(tsh1) == product(tsh2)
    assert fi1

    assert all(i in fi1 for i in fi2)

    nmui = len(multiindex)
    assert nmui == len(sh2)

    # Build map from fi2/fid2 position (-offset nmui) to fi1/fid1 position
    p2_to_p1_map = [None] * r2
    for k, i in enumerate(fi2):
        p2_to_p1_map[k + nmui] = fi1.index(i)

    # Build map from fi1/fid1 position to mi position
    for k, i in enumerate(mi):
        p2_to_p1_map[k] = fi1.index(mi[k].count())

    # Build map from flattened e1 component to flattened e2 component
    perm2 = compute_indices(tsh2)
    ni2 = product(tsh2)

    # Situation: e2 = as_tensor(e1, mi)
    d2 = [None] * ni2
    p1 = [None] * r1
    for c2, p2 in enumerate(perm2):
        for k2, k1 in enumerate(p2_to_p1_map):
            p1[k1] = p2[k2]
        c1 = flatten_multiindex(p1, str1)
        d2[c2] = c1

    # Consistency checks
    assert all(isinstance(x, int) for x in d2)
    assert len(set(d2)) == len(d2)
    return d2
예제 #17
0
파일: indexing.py 프로젝트: FEniCS/ffc
def map_indexed_arg_components(indexed):
    """Build integer list mapping between flattended components
    of indexed expression and its underlying tensor-valued subexpression."""

    assert isinstance(indexed, Indexed)

    # AKA indexed = tensor[multiindex]
    tensor, multiindex = indexed.ufl_operands

    # AKA e1 = e2[multiindex]
    # (this renaming is historical, but kept for consistency with all the variables *1,*2 below)
    e2 = tensor
    e1 = indexed

    # Get tensor and index shape
    sh1 = e1.ufl_shape
    sh2 = e2.ufl_shape
    fi1 = e1.ufl_free_indices
    fi2 = e2.ufl_free_indices
    fid1 = e1.ufl_index_dimensions
    fid2 = e2.ufl_index_dimensions

    # Compute regular and total shape
    tsh1 = sh1 + fid1
    tsh2 = sh2 + fid2
    # r1 = len(tsh1)
    r2 = len(tsh2)
    # str1 = shape_to_strides(tsh1)
    str2 = shape_to_strides(tsh2)
    assert not sh1
    assert sh2  # Must have shape to be indexed in the first place
    assert product(tsh1) <= product(tsh2)

    # Build map from fi2/fid2 position (-offset nmui) to fi1/fid1 position
    ind2_to_ind1_map = [None] * len(fi2)
    for k, i in enumerate(fi2):
        ind2_to_ind1_map[k] = fi1.index(i)

    # Build map from fi1/fid1 position to mi position
    nmui = len(multiindex)
    multiindex_to_ind1_map = [None] * nmui
    for k, i in enumerate(multiindex):
        if isinstance(i, Index):
            multiindex_to_ind1_map[k] = fi1.index(i.count())

    # Build map from flattened e1 component to flattened e2 component
    perm1 = compute_indices(tsh1)
    ni1 = product(tsh1)

    # Situation: e1 = e2[mi]
    d1 = [None] * ni1
    p2 = [None] * r2
    assert len(sh2) == nmui
    for k, i in enumerate(multiindex):
        if isinstance(i, FixedIndex):
            p2[k] = int(i)
    for c1, p1 in enumerate(perm1):
        for k, i in enumerate(multiindex):
            if isinstance(i, Index):
                p2[k] = p1[multiindex_to_ind1_map[k]]
        for k, i in enumerate(ind2_to_ind1_map):
            p2[nmui + k] = p1[i]
        c2 = flatten_multiindex(p2, str2)
        d1[c1] = c2

    # Consistency checks
    assert all(isinstance(x, int) for x in d1)
    assert len(set(d1)) == len(d1)
    return d1