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) conditionals = [i for i in ordering if i.is_Conditional] # The iteration space is constructed so that information always flows # from an iteration to another (i.e., no anti-dependences are created) directions, _ = force_directions(detect_flow_directions(expr), lambda i: Any) iterators = build_iterators(mapper) intervals = build_intervals(Stencil.union(*mapper.values())) intervals = IntervalGroup(intervals, relations=ordering.relations) ispace = IterationSpace(intervals.zero(), iterators, directions) # The data space is relative to the computational domain. Note that we # are deliberately dropping the intervals ordering (by turning `intervals` # into a list), as this is irrelevant (even more: dangerous) for data spaces intervals = [i if i.dim in oobs else i.zero() for i in intervals] intervals += [Interval(i, 0, 0) for i in ordering if i not in ispace.dimensions + conditionals] parts = {k: IntervalGroup(build_intervals(v)) for k, v in mapper.items() if k} dspace = DataSpace(intervals, parts) # Finally create the LoweredEq with all metadata attached expr = super(LoweredEq, cls).__new__(cls, expr.lhs, expr.rhs, evaluate=False) expr._dspace = dspace expr._ispace = ispace expr._conditionals = tuple(conditionals) expr._reads, expr._writes = detect_io(expr) expr._is_Increment = input_expr.is_Increment expr._implicit_dims = input_expr.implicit_dims return expr
def __new__(cls, *args, **kwargs): if len(args) == 1 and isinstance(args[0], LoweredEq): # origin: LoweredEq(devito.LoweredEq, **kwargs) input_expr = args[0] expr = 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(sympy.Eq) input_expr = expr = args[0] elif len(args) == 2: expr = 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) conditionals = [i for i in ordering if i.is_Conditional] # The iteration space is constructed so that information always flows # from an iteration to another (i.e., no anti-dependences are created) directions, _ = force_directions(detect_flow_directions(expr), lambda i: Any) iterators = build_iterators(mapper) intervals = build_intervals(Stencil.union(*mapper.values())) intervals = IntervalGroup(intervals, relations=ordering.relations) ispace = IterationSpace(intervals.zero(), iterators, directions) # The data space is relative to the computational domain. Note that we # are deliberately dropping the intervals ordering (by turning `intervals` # into a list), as this is irrelevant (even more: dangerous) for data spaces intervals = [i if i.dim in oobs else i.zero() for i in intervals] intervals += [Interval(i, 0, 0) for i in ordering if i not in ispace.dimensions + conditionals] parts = {k: IntervalGroup(build_intervals(v)) for k, v in mapper.items() if k} dspace = DataSpace(intervals, parts) # Finally create the LoweredEq with all metadata attached expr = super(LoweredEq, cls).__new__(cls, expr.lhs, expr.rhs, evaluate=False) expr._is_Increment = getattr(input_expr, 'is_Increment', False) expr._dspace = dspace expr._ispace = ispace expr._conditionals = tuple(conditionals) expr._reads, expr._writes = detect_io(expr) return expr
def dspace(self): """ Derive the DataSpace of the Cluster from its expressions, IterationSpace, and Guards. """ accesses = detect_accesses(self.exprs) # Construct the `parts` of the DataSpace, that is a projection of the data # space for each Function appearing in `self.exprs` parts = {} for f, v in accesses.items(): if f is None: continue intervals = [ Interval(d, min(offs), max(offs)) for d, offs in v.items() ] intervals = IntervalGroup(intervals) # Factor in the IterationSpace -- if the min/max points aren't zero, # then the data intervals need to shrink/expand accordingly intervals = intervals.promote(lambda d: d.is_Block) shift = self.ispace.intervals.promote(lambda d: d.is_Block) intervals = intervals.add(shift) # Map SubIterators to the corresponding data space Dimension # E.g., `xs -> x -> x0_blk0 -> x` or `t0 -> t -> time` intervals = intervals.promote(lambda d: d.is_SubIterator) # If the bound of a Dimension is explicitly guarded, then we should # shrink the `parts` accordingly for d, v in self.guards.items(): ret = v.find(BaseGuardBoundNext) assert len(ret) <= 1 if len(ret) != 1: continue if ret.pop().direction is Forward: intervals = intervals.translate(d, v1=-1) else: intervals = intervals.translate(d, 1) # Special case: if the factor of a ConditionalDimension has value 1, # then we can safely resort to the parent's Interval intervals = intervals.promote( lambda d: d.is_Conditional and d.factor == 1) parts[f] = intervals # Determine the Dimensions requiring shifted min/max points to avoid # OOB accesses oobs = set() for f, v in parts.items(): for i in v: if i.dim.is_Sub: d = i.dim.parent else: d = i.dim try: if i.lower < 0 or \ i.upper > f._size_nodomain[d].left + f._size_halo[d].right: # It'd mean trying to access a point before the # left halo (test0) or after the right halo (test1) oobs.update(d._defines) except (KeyError, TypeError): # Unable to detect presence of OOB accesses (e.g., `d` not in # `f._size_halo`, that is typical of indirect accesses `A[B[i]]`) pass # Construct the `intervals` of the DataSpace, that is a global, # Dimension-centric view of the data space intervals = IntervalGroup.generate('union', *parts.values()) # E.g., `db0 -> time`, but `xi NOT-> x` intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) return DataSpace(intervals, parts)