コード例 #1
0
ファイル: space.py プロジェクト: alansaillet/devito-1
 def __init__(self, intervals, sub_iterators=None, directions=None):
     super(IterationSpace, self).__init__(intervals)
     self._sub_iterators = frozendict(sub_iterators or {})
     if directions is None:
         self._directions = frozendict([(i.dim, Any) for i in self.intervals])
     else:
         self._directions = frozendict(directions)
コード例 #2
0
ファイル: halo_scheme.py プロジェクト: carlosjedwab/MyDevito
    def __init__(self, exprs, ispace):
        # Derive halo exchanges
        self._mapper = {}
        scope = Scope(exprs)
        for f, (halos, local) in classify(scope).items():
            if halos:
                loc_indices = compute_local_indices(f, local, ispace, scope)
                self._mapper[f] = HaloSchemeEntry(frozendict(loc_indices),
                                                  frozenset(halos))
        self._mapper = frozendict(self._mapper)

        # Track the IterationSpace offsets induced by SubDomains/SubDimensions.
        # These should be honored in the derivation of the `omapper`
        self._honored = {}
        # SubDimensions are not necessarily included directly in
        # ispace.dimensions and hence we need to first utilize the `_defines` method
        dims = set().union(*[
            d._defines for d in ispace.dimensions
            if d._defines & self.dimensions
        ])
        subdims = [d for d in dims if d.is_Sub and not d.local]
        for i in subdims:
            ltk, _ = i.thickness.left
            rtk, _ = i.thickness.right
            self._honored[i.root] = frozenset([(ltk, rtk)])
        self._honored = frozendict(self._honored)
コード例 #3
0
    def __new__(cls, expr, *dims, **kwargs):
        if type(expr) == sympy.Derivative:
            raise ValueError(
                "Cannot nest sympy.Derivative with devito.Derivative")
        if not isinstance(expr, Differentiable):
            raise ValueError("`expr` must be a Differentiable object")

        new_dims, orders, fd_o, var_count = cls._process_kwargs(
            expr, *dims, **kwargs)

        # Construct the actual Derivative object
        obj = Differentiable.__new__(cls, expr, *var_count)
        obj._dims = tuple(OrderedDict.fromkeys(new_dims))

        skip = kwargs.get('preprocessed', False) or obj.ndims == 1

        obj._fd_order = fd_o if skip else DimensionTuple(*fd_o,
                                                         getters=obj._dims)
        obj._deriv_order = orders if skip else DimensionTuple(
            *orders, getters=obj._dims)
        obj._side = kwargs.get("side")
        obj._transpose = kwargs.get("transpose", direct)
        obj._ppsubs = as_tuple(frozendict(i) for i in kwargs.get("subs", []))
        obj._x0 = frozendict(kwargs.get('x0', {}))
        return obj
コード例 #4
0
    def __init__(self, exprs, ispace, dspace, guards=None, properties=None):
        self._exprs = tuple(ClusterizedEq(i, ispace=ispace, dspace=dspace)
                            for i in as_tuple(exprs))
        self._ispace = ispace
        self._dspace = dspace
        self._guards = frozendict(guards or {})

        properties = dict(properties or {})
        properties.update({i.dim: properties.get(i.dim, set()) for i in ispace.intervals})
        self._properties = frozendict(properties)
コード例 #5
0
ファイル: space.py プロジェクト: pnmoralesh/Devito
    def __init__(self, intervals, sub_iterators=None, directions=None):
        super(IterationSpace, self).__init__(intervals)

        # Normalize sub-iterators
        sub_iterators = sub_iterators or {}
        self._sub_iterators = frozendict([(k, tuple(filter_ordered(as_tuple(v))))
                                          for k, v in sub_iterators.items()])

        # Normalize directions
        if directions is None:
            self._directions = frozendict([(i.dim, Any) for i in self.intervals])
        else:
            self._directions = frozendict(directions)
コード例 #6
0
ファイル: misc.py プロジェクト: ofmla/devito
    def _key(self, c):
        # Two Clusters/ClusterGroups are fusion candidates if their key is identical

        key = (frozenset(c.itintervals), c.guards)

        # We allow fusing Clusters/ClusterGroups even in presence of WaitLocks and
        # WithLocks, but not with any other SyncOps
        if isinstance(c, Cluster):
            sync_locks = (c.sync_locks, )
        else:
            sync_locks = c.sync_locks
        for i in sync_locks:
            mapper = defaultdict(set)
            for k, v in i.items():
                for s in v:
                    if s.is_WaitLock or \
                       (self.fusetasks and s.is_WithLock):
                        mapper[k].add(type(s))
                    else:
                        mapper[k].add(s)
                mapper[k] = frozenset(mapper[k])
            mapper = frozendict(mapper)
            key += (mapper, )

        return key
コード例 #7
0
ファイル: asynchrony.py プロジェクト: kenhester/devito
def actions_from_unstructured(clusters, key, prefix, actions):
    it = prefix[-1]
    d = it.dim
    direction = it.direction

    # Locate the streamable Functions
    first_seen = {}
    last_seen = {}
    for c in clusters:
        candidates = key(c)
        if not candidates:
            continue
        for i in c.scope.accesses:
            f = i.function
            if f in candidates:
                k = (f, i[d])
                first_seen.setdefault(k, c)
                last_seen[k] = c
    if not first_seen:
        return clusters

    callbacks = [(frozendict(first_seen), FetchPrefetch),
                 (frozendict(last_seen), Delete)]

    # Create and map SyncOps to Clusters
    for seen, callback in callbacks:
        mapper = defaultdict(lambda: DefaultOrderedDict(list))
        for (f, v), c in seen.items():
            mapper[c][f].append(v)

        for c, m in mapper.items():
            for f, v in m.items():
                for fetch, s in indices_to_sections(v):
                    if direction is Forward:
                        ifetch = fetch.subs(d, d.symbolic_min)
                        fcond = make_cond(c.guards.get(d), d, direction, d.symbolic_min)
                        pfetch = fetch + 1
                        pcond = make_cond(c.guards.get(d), d, direction, d + 1)
                    else:
                        ifetch = fetch.subs(d, d.symbolic_max)
                        fcond = make_cond(c.guards.get(d), d, direction, d.symbolic_max)
                        pfetch = fetch - 1
                        pcond = make_cond(c.guards.get(d), d, direction, d - 1)

                    syncop = callback(d, s, f, fetch, ifetch, fcond, pfetch, pcond)
                    actions[c].syncs[d].append(syncop)
コード例 #8
0
ファイル: halo_scheme.py プロジェクト: pnmoralesh/Devito
    def __init__(self, exprs, ispace):
        # Derive the halo exchanges
        self._mapper = frozendict(classify(exprs, ispace))

        # Track the IterationSpace offsets induced by SubDomains/SubDimensions.
        # These should be honored in the derivation of the `omapper`
        self._honored = {}
        # SubDimensions are not necessarily included directly in
        # ispace.dimensions and hence we need to first utilize the `_defines` method
        dims = set().union(*[d._defines for d in ispace.dimensions
                             if d._defines & self.dimensions])
        subdims = [d for d in dims if d.is_Sub and not d.local]
        for i in subdims:
            ltk, _ = i.thickness.left
            rtk, _ = i.thickness.right
            self._honored[i.root] = frozenset([(ltk, rtk)])
        self._honored = frozendict(self._honored)
コード例 #9
0
ファイル: aliases.py プロジェクト: ofmla/devito
    def _lookup_key(self, c, d):
        ispace = c.ispace.reset()
        dintervals = c.dspace.intervals.drop(d).reset()
        properties = frozendict(
            {d: relax_properties(v)
             for d, v in c.properties.items()})

        return AliasKey(ispace, dintervals, c.dtype, None, properties)
コード例 #10
0
ファイル: cluster.py プロジェクト: speglich/devito
    def __init__(self,
                 exprs,
                 ispace=None,
                 guards=None,
                 properties=None,
                 syncs=None):
        ispace = ispace or IterationSpace([])

        self._exprs = tuple(
            ClusterizedEq(e, ispace=ispace) for e in as_tuple(exprs))
        self._ispace = ispace
        self._guards = frozendict(guards or {})
        self._syncs = frozendict(syncs or {})

        properties = dict(properties or {})
        properties.update(
            {i.dim: properties.get(i.dim, set())
             for i in ispace.intervals})
        self._properties = frozendict(properties)
コード例 #11
0
ファイル: halo_scheme.py プロジェクト: rawsh/devito
    def __init__(self, exprs, ispace):
        # Derive halo exchanges
        self._mapper = {}
        scope = Scope(exprs)
        for f, (halos, local) in classify(scope).items():
            if halos:
                loc_indices = compute_local_indices(f, local, ispace, scope)
                self._mapper[f] = HaloSchemeEntry(frozendict(loc_indices),
                                                  frozenset(halos))
        self._mapper = frozendict(self._mapper)

        # Track the IterationSpace offsets induced by SubDomains/SubDimensions.
        # These should be honored in the derivation of the `omapper`
        self._honored = {}
        for i in ispace.intervals:
            if not i.dim._defines & set(self.dimensions):
                continue
            elif i.dim.is_Sub and not i.dim.local:
                ltk, _ = i.dim.thickness.left
                rtk, _ = i.dim.thickness.right
                self._honored[i.dim.root] = frozenset([(ltk, rtk)])
        self._honored = frozendict(self._honored)
コード例 #12
0
ファイル: misc.py プロジェクト: nogueirapeterson/devito
    def _key(self, c):
        # Two Clusters/ClusterGroups are fusion candidates if their key is identical

        key = (frozenset(c.itintervals), c.guards)

        # We allow fusing Clusters/ClusterGroups with WaitLocks over different Locks,
        # while the WithLocks are to be kept separated (i.e. the remain separate tasks)
        if isinstance(c, Cluster):
            sync_locks = (c.sync_locks,)
        else:
            sync_locks = c.sync_locks
        for i in sync_locks:
            key += (frozendict({k: frozenset(type(i) if i.is_WaitLock else i for i in v)
                                for k, v in i.items()}),)

        return key
コード例 #13
0
ファイル: basic.py プロジェクト: kristiantorres/devito
    def _cache_key(cls, *args, **kwargs):
        args = list(args)
        key = {}

        # The base type is necessary, otherwise two objects such as
        # `Scalar(name='s')` and `Dimension(name='s')` would have the same key
        key['cls'] = cls

        # The name is always present, and added as if it were an arg
        key['name'] = kwargs.pop('name', None) or args.pop(0)

        # From the args
        key['args'] = tuple(args)

        # From the kwargs
        key.update(kwargs)

        return frozendict(key)
コード例 #14
0
ファイル: halo_scheme.py プロジェクト: pnmoralesh/Devito
 def build(cls, fmapper, honored):
     obj = object.__new__(HaloScheme)
     obj._mapper = frozendict(fmapper)
     obj._honored = frozendict(honored)
     return obj
コード例 #15
0
 def conditionals(self):
     return self._conditionals or frozendict()
コード例 #16
0
ファイル: halo_scheme.py プロジェクト: pnmoralesh/Devito
def classify(exprs, ispace):
    """
    Produce the mapper ``Function -> HaloSchemeEntry``, which describes the
    necessary halo exchanges in the given Scope.
    """
    scope = Scope(exprs)

    mapper = {}
    for f, r in scope.reads.items():
        if not f.is_DiscreteFunction:
            continue
        elif f.grid is None:
            # TODO: improve me
            continue

        # For each data access, determine if (and what type of) a halo exchange
        # is required
        halo_labels = defaultdict(set)
        for i in r:
            v = {}
            for d in i.findices:
                if f.grid.is_distributed(d):
                    if i.affine(d):
                        thl, thr = i.touched_halo(d)
                        # Note: if the left-HALO is touched (i.e., `thl = True`), then
                        # the *right-HALO* is to be sent over in a halo exchange
                        v[(d, LEFT)] = (thr and STENCIL) or IDENTITY
                        v[(d, RIGHT)] = (thl and STENCIL) or IDENTITY
                    else:
                        v[(d, LEFT)] = STENCIL
                        v[(d, RIGHT)] = STENCIL
                else:
                    v[(d, i.aindices[d])] = NONE

            # Does `i` actually require a halo exchange?
            if not any(hl is STENCIL for hl in v.values()):
                continue

            # Derive diagonal halo exchanges from the previous analysis
            combs = list(product([LEFT, CENTER, RIGHT], repeat=len(f._dist_dimensions)))
            combs.remove((CENTER,)*len(f._dist_dimensions))
            for c in combs:
                key = (f._dist_dimensions, c)
                if all(v.get((d, s)) is STENCIL or s is CENTER for d, s in zip(*key)):
                    v[key] = STENCIL

            # Finally update the `halo_labels`
            for j, hl in v.items():
                halo_labels[j].add(hl)

        if not halo_labels:
            continue

        # Distinguish between Dimensions requiring a halo exchange and those which don't
        up_loc_indices, halos = defaultdict(list), []
        for (d, s), hl in halo_labels.items():
            try:
                hl.remove(IDENTITY)
            except KeyError:
                pass
            if not hl:
                continue
            elif len(hl) > 1:
                raise HaloSchemeException("Inconsistency found while building a halo "
                                          "scheme for `%s` along Dimension `%s`" % (f, d))
            elif hl.pop() is STENCIL:
                halos.append(Halo(d, s))
            else:
                up_loc_indices[d].append(s)

        # Process the loc_indices. Consider:
        # 1) u[t+1, x] = f(u[t, x])   => shift == 1
        # 2) u[t-1, x] = f(u[t, x])   => shift == 1
        # 3) u[t+1, x] = f(u[t+1, x]) => shift == 0
        # In the first and second cases, the x-halo should be inserted at `t`,
        # while in the last case it should be inserted at `t+1`.
        loc_indices = {}
        for d, aindices in up_loc_indices.items():
            try:
                func = Max if ispace.is_forward(d.root) else Min
            except KeyError:
                # Max or Min is the same since `d` isn't an `ispace` Dimension
                func = Max
            candidates = [i for i in aindices if not is_integer(i)]
            candidates = {(i.origin if d.is_Stepping else i) - d: i for i in candidates}
            loc_indices[d] = candidates[func(*candidates.keys())]

        mapper[f] = HaloSchemeEntry(frozendict(loc_indices), frozenset(halos))

    return mapper
コード例 #17
0
ファイル: halo_scheme.py プロジェクト: pnmoralesh/Devito
    def omapper(self):
        """
        Logical decomposition of the DOMAIN region into OWNED and CORE sub-regions.

        This is "cumulative" over all DiscreteFunctions in the HaloScheme; it also
        takes into account IterationSpace offsets induced by SubDomains/SubDimensions.

        Examples
        --------
        Consider a HaloScheme comprising two one-dimensional Functions, ``u``
        and ``v``.  ``u``'s halo, on the LEFT and RIGHT DataSides respectively,
        is (2, 2), while ``v``'s is (4, 4). The situation is depicted below.

              ^^oo----------------oo^^     u
            ^^^^oooo------------oooo^^^^   v

        Where '^' represents a HALO point, 'o' a OWNED point, and '-' a CORE point.
        Together, the 'o' and '-' points constitute the DOMAIN region.

        In this example, the "cumulative" OWNED size is (left=4, right=4), that is
        the max on each DataSide across all Functions, namely ``u`` and ``v``.

        The ``omapper`` will contain the following entries:

            [(((d, CORE, CENTER),), {d: (d_m + 4, d_M - 4)}),
             (((d, OWNED, LEFT),), {d: (d_m, min(d_m + 3, d_M))}),
             (((d, OWNED, RIGHT),), {d: (max(d_M - 3, d_m), d_M)})]

        In presence of SubDomains (or, more generally, iteration over SubDimensions),
        the "true" DOMAIN is actually smaller. For example, consider again the
        example above, but now with a SubDomain that excludes the first ``nl``
        and the last ``nr`` DOMAIN points, where ``nl >= 0`` and ``nr >= 0``. Often,
        ``nl`` and ``nr`` are referred to as the "thickness" of the SubDimension (see
        also SubDimension.__doc__). For example, the situation could be as below

              ^^ooXXX----------XXXoo^^     u
            ^^^^ooooX----------Xoooo^^^^   v

        Where 'X' is a CORE point excluded by the computation due to the SubDomain.
        Here, the 'o' points are outside of the SubDomain, but in general they could
        also be inside. The ``omapper`` is constructed taking into account that
        SubDomains are iterated over with min point ``d_m + nl`` and max point
        ``d_M - nr``. Here, the ``omapper`` is:

            [(((d, CORE, CENTER),), {d: (d_m + 4, d_M - 4),
                                     nl: (max(nl - 4, 0),),
                                     nr: (max(nr - 4, 0),)}),
             (((d, OWNED, LEFT),), {d: (d_m, min(d_m + 3, d_M - nr)),
                                    nl: (nl,),
                                    nr: (0,)}),
             (((d, OWNED, RIGHT),), {d: (max(d_M - 3, d_m + nl), d_M),
                                     nl: (0,),
                                     nr: (nr,)})]

        To convince ourselves that this makes sense, we consider a number of cases.
        For now, we assume ``|d_M - d_m| > HALO``, that is the left-HALO and right-HALO
        regions do not overlap.

            1. The SubDomain thickness is 0, which is like there were no SubDomains.
               By instantiating the template above with ``nl = 0`` and ``nr = 0``,
               it is trivial to see that we fall back to the non-SubDomain case.

            2. The SubDomain thickness is as big as the HALO region size, that is
               ``nl = 4`` and ``nr = 4``. The ``omapper`` is such that no iterations
               will be performed in the OWNED regions (i.e., "everything is CORE").

            3. The SubDomain left-thickness is smaller than the left-HALO region size,
               while the SubDomain right-thickness is larger than the right-Halo region
               size. This means that some left-OWNED points are within the SubDomain,
               while the RIGHT-OWNED are outside. For example, take ``nl = 1`` and
               ``nr = 5``; the iteration regions will then be:

                - (CORE, CENTER): {d: (d_m + 4, d_M - 4), nl: (0,), nr: (1,)}, so
                  the min point is ``d_m + 4``, while the max point is ``d_M - 5``.

                - (OWNED, LEFT): {d: (d_m, d_m + 3), nl: (1,), nr: (0,)}, so the
                  min point is ``d_m + 1``, while the max point is ``dm + 3``.

                - (OWNED, RIGHT): {d: (d_M - 3, d_M), nl: (0,), nr: (5,)}, so the
                  min point is ``d_M - 3``, while the max point is ``d_M - 5``,
                  which implies zero iterations in this region.

        Let's now assume that the left-HALO and right-HALO regions overlap. For example,
        ``d_m = 0`` and ``d_M = 1`` (i.e., the DOMAIN only has two points), with the HALO
        size that is still (4, 4).

            4. Let's take ``nl = 1`` and ``nr = 0``. That is, only one point is in
               the SubDomain and should be updated. We again instantiate the iteration
               regions and obtain:

                - (CORE, CENTER): {d: (d_m + 4, d_M - 4), nl: (0,), nr: (0,)}, so
                  the min point is ``d_m + 4 = 4``, while the max point is
                  ``d_M - 4 = -3``, which implies zero iterations in this region.

                - (OWNED, LEFT): {d: (d_m, min(d_m + 3, d_M - nr)), nl: (1,), nr: (0,)},
                  so the min point is ``d_m + 1 = 1``, while the max point is
                  ``min(d_m + 3, d_M - nr) = min(3, 1) = 1``, which implies that there
                  is exactly one point in this region.

                - (OWNED, RIGHT): {d: (max(d_M - 3, d_m + nl), d_M), nl: (0,), nr: (0,)},
                  so the min point is ``max(d_M - 3, d_m + nl) = max(-2, 1) = 1``, while
                  the max point is ``d_M = 1``, which implies that there is exactly one
                  point in this region, and this point is redundantly computed as it's
                  logically the same as that in the (OWNED, LEFT) region.

        Notes
        -----
        For each Function, the '^' and 'o' are exactly the same on *all MPI
        ranks*, so the output of this method is guaranteed to be consistent
        across *all MPI ranks*.
        """
        items = [((d, CENTER), (d, LEFT), (d, RIGHT)) for d in self.dimensions]

        processed = []
        for item in product(*items):
            where = []
            mapper = {}
            for d, s in item:
                osl, osr = self.owned_size[d]

                # Handle SubDomain/SubDimensions to-honor offsets
                nl = Max(0, *[i for i, _ in self.honored.get(d, [])])
                nr = Max(0, *[i for _, i in self.honored.get(d, [])])

                if s is CENTER:
                    where.append((d, CORE, s))
                    mapper[d] = (d.symbolic_min + osl,
                                 d.symbolic_max - osr)
                    if nl != 0:
                        mapper[nl] = (Max(nl - osl, 0),)
                    if nr != 0:
                        mapper[nr] = (Max(nr - osr, 0),)
                else:
                    where.append((d, OWNED, s))
                    if s is LEFT:
                        mapper[d] = (d.symbolic_min,
                                     Min(d.symbolic_min + osl - 1, d.symbolic_max - nr))
                        if nl != 0:
                            mapper[nl] = (nl,)
                            mapper[nr] = (0,)
                    else:
                        mapper[d] = (Max(d.symbolic_max - osr + 1, d.symbolic_min + nl),
                                     d.symbolic_max)
                        if nr != 0:
                            mapper[nl] = (0,)
                            mapper[nr] = (nr,)
            processed.append((tuple(where), frozendict(mapper)))

        _, core = processed.pop(0)
        owned = processed

        return OMapper(core, owned)
コード例 #18
0
    def callback(self, clusters, prefix):
        if not prefix:
            return clusters

        it = prefix[-1]
        d = it.dim
        direction = it.direction

        try:
            pd = prefix[-2].dim
        except IndexError:
            pd = None

        # What are the stream-able Dimensions?
        # 0) all sequential Dimensions
        # 1) all CustomDimensions of fixed (i.e. integer) size, which
        #    implies a bound on the amount of streamed data
        if all(SEQUENTIAL in c.properties[d] for c in clusters):
            make_fetch = lambda f, i, s, cb: FetchWaitPrefetch(
                f, d, direction, i, s, cb)
            make_delete = lambda f, i, s, cb: Delete(f, d, direction, i, s, cb)
            syncd = d
        elif d.is_Custom and is_integer(it.size):
            make_fetch = lambda f, i, s, cb: FetchWait(f, d, direction, i, it.
                                                       size, cb)
            make_delete = lambda f, i, s, cb: Delete(f, d, direction, i, it.
                                                     size, cb)
            syncd = pd
        else:
            return clusters

        first_seen = {}
        last_seen = {}
        for c in clusters:
            candidates = self.key(c)
            if not candidates:
                continue
            for i in c.scope.accesses:
                f = i.function
                if f in candidates:
                    k = (f, i[d])
                    first_seen.setdefault(k, c)
                    last_seen[k] = c

        if not first_seen:
            return clusters

        # Bind fetches and deletes to Clusters
        sync_ops = defaultdict(list)
        callbacks = [(frozendict(first_seen), make_fetch),
                     (frozendict(last_seen), make_delete)]
        for seen, callback in callbacks:
            mapper = defaultdict(lambda: DefaultOrderedDict(list))
            for (f, v), c in seen.items():
                mapper[c][f].append(v)
            for c, m in mapper.items():
                for f, v in m.items():
                    for i, s in indices_to_sections(v):
                        next_cbk = make_next_cbk(c.guards.get(d), d, direction)
                        sync_ops[c].append(callback(f, i, s, next_cbk))

        # Attach SyncOps to Clusters
        processed = []
        for c in clusters:
            v = sync_ops.get(c)
            if v is not None:
                processed.append(
                    c.rebuild(syncs=normalize_syncs(c.syncs, {syncd: v})))
            else:
                processed.append(c)

        return processed
コード例 #19
0
 def sync_locks(self):
     return frozendict({
         k: tuple(i for i in v if i.is_SyncLock)
         for k, v in self.syncs.items()
     })
コード例 #20
0
ファイル: equation.py プロジェクト: carlosjedwab/MyDevito
    def __new__(cls, *args, **kwargs):
        if len(args) == 1 and isinstance(args[0], LoweredEq):
            # origin: LoweredEq(devito.LoweredEq, **kwargs)
            input_expr = args[0]
            expr = sympy.Eq.__new__(cls, *input_expr.args, evaluate=False)
            for i in cls._state:
                setattr(expr, '_%s' % i,
                        kwargs.get(i) or getattr(input_expr, i))
            return expr
        elif len(args) == 1 and isinstance(args[0], Eq):
            # origin: LoweredEq(devito.Eq)
            input_expr = expr = args[0]
        elif len(args) == 2:
            expr = sympy.Eq.__new__(cls, *args, evaluate=False)
            for i in cls._state:
                setattr(expr, '_%s' % i, kwargs.pop(i))
            return expr
        else:
            raise ValueError("Cannot construct LoweredEq from args=%s "
                             "and kwargs=%s" % (str(args), str(kwargs)))

        # Well-defined dimension ordering
        ordering = dimension_sort(expr)

        # Analyze the expression
        mapper = detect_accesses(expr)
        oobs = detect_oobs(mapper)
        conditional_dimensions = [i for i in ordering if i.is_Conditional]

        # Construct Intervals for IterationSpace and DataSpace
        intervals = build_intervals(Stencil.union(*mapper.values()))
        iintervals = []  # iteration Intervals
        dintervals = []  # data Intervals
        for i in intervals:
            d = i.dim
            if d in oobs:
                iintervals.append(i.zero())
                dintervals.append(i)
            else:
                iintervals.append(i.zero())
                dintervals.append(i.zero())

        # Construct the IterationSpace
        iintervals = IntervalGroup(iintervals, relations=ordering.relations)
        iterators = build_iterators(mapper)
        ispace = IterationSpace(iintervals, iterators)

        # Construct the DataSpace
        dintervals.extend([
            Interval(i, 0, 0) for i in ordering
            if i not in ispace.dimensions + conditional_dimensions
        ])
        parts = {
            k: IntervalGroup(build_intervals(v)).add(iintervals)
            for k, v in mapper.items() if k
        }
        dspace = DataSpace(dintervals, parts)

        # Construct the conditionals
        conditionals = {}
        for d in conditional_dimensions:
            if d.condition is None:
                conditionals[d] = CondEq(d.parent % d.factor, 0)
            else:
                conditionals[d] = lower_exprs(d.condition)
        conditionals = frozendict(conditionals)

        # Lower all Differentiable operations into SymPy operations
        rhs = diff2sympy(expr.rhs)

        # Finally create the LoweredEq with all metadata attached
        expr = super(LoweredEq, cls).__new__(cls,
                                             expr.lhs,
                                             rhs,
                                             evaluate=False)

        expr._dspace = dspace
        expr._ispace = ispace
        expr._conditionals = conditionals
        expr._reads, expr._writes = detect_io(expr)

        expr._is_Increment = input_expr.is_Increment
        expr._implicit_dims = input_expr.implicit_dims

        return expr
コード例 #21
0
    def __new__(cls, *args, **kwargs):
        if len(args) == 1 and isinstance(args[0], LoweredEq):
            # origin: LoweredEq(devito.LoweredEq, **kwargs)
            input_expr = args[0]
            expr = sympy.Eq.__new__(cls, *input_expr.args, evaluate=False)
            for i in cls._state:
                setattr(expr, '_%s' % i,
                        kwargs.get(i) or getattr(input_expr, i))
            return expr
        elif len(args) == 1 and isinstance(args[0], Eq):
            # origin: LoweredEq(devito.Eq)
            input_expr = expr = args[0]
        elif len(args) == 2:
            expr = sympy.Eq.__new__(cls, *args, evaluate=False)
            for i in cls._state:
                setattr(expr, '_%s' % i, kwargs.pop(i))
            return expr
        else:
            raise ValueError("Cannot construct LoweredEq from args=%s "
                             "and kwargs=%s" % (str(args), str(kwargs)))

        # Well-defined dimension ordering
        ordering = dimension_sort(expr)

        # Analyze the expression
        accesses = detect_accesses(expr)
        dimensions = Stencil.union(*accesses.values())

        # Separate out the SubIterators from the main iteration Dimensions, that
        # is those which define an actual iteration space
        iterators = {}
        for d in dimensions:
            if d.is_SubIterator:
                iterators.setdefault(d.root, set()).add(d)
            elif d.is_Conditional:
                # Use `parent`, and `root`, because a ConditionalDimension may
                # have a SubDimension as parent
                iterators.setdefault(d.parent, set())
            else:
                iterators.setdefault(d, set())

        # Construct the IterationSpace
        intervals = IntervalGroup([Interval(d, 0, 0) for d in iterators],
                                  relations=ordering.relations)
        ispace = IterationSpace(intervals, iterators)

        # Construct the conditionals and replace the ConditionalDimensions in `expr`
        conditionals = {}
        for d in ordering:
            if not d.is_Conditional:
                continue
            if d.condition is None:
                conditionals[d] = GuardFactor(d)
            else:
                conditionals[d] = diff2sympy(lower_exprs(d.condition))
            if d.factor is not None:
                expr = uxreplace(expr, {d: IntDiv(d.index, d.factor)})
        conditionals = frozendict(conditionals)

        # Lower all Differentiable operations into SymPy operations
        rhs = diff2sympy(expr.rhs)

        # Finally create the LoweredEq with all metadata attached
        expr = super(LoweredEq, cls).__new__(cls,
                                             expr.lhs,
                                             rhs,
                                             evaluate=False)

        expr._ispace = ispace
        expr._conditionals = conditionals
        expr._reads, expr._writes = detect_io(expr)

        expr._is_Increment = input_expr.is_Increment
        expr._implicit_dims = input_expr.implicit_dims

        return expr
コード例 #22
0
 def __init__(self, intervals, parts):
     super(DataSpace, self).__init__(intervals)
     self._parts = frozendict(parts)