def inject(self, field, expr, offset=0): """ Generate equations injecting an arbitrary expression into a field. Parameters ---------- field : Function Input field into which the injection is performed. expr : expr-like Injected expression. offset : int, optional Additional offset from the boundary. """ expr = indexify(expr) field = indexify(field) p, _ = self.gridpoints.indices dim_subs = [] coeffs = [] for i, d in enumerate(self.grid.dimensions): rd = DefaultDimension(name="r%s" % d.name, default_value=self.r) dim_subs.append((d, INT(rd + self.gridpoints[p, i]))) coeffs.append(self.interpolation_coeffs[p, i, rd]) rhs = prod(coeffs) * expr field = field.subs(dim_subs) return [Eq(field, field + rhs.subs(dim_subs))]
def inject(self, field, expr, offset=0): """ Generate equations injecting an arbitrary expression into a field. Parameters ---------- field : Function Input field into which the injection is performed. expr : expr-like Injected expression. offset : int, optional Additional offset from the boundary. """ expr = indexify(expr) field = indexify(field) p, _ = self.gridpoints.indices dim_subs = [] coeffs = [] for i, d in enumerate(self.grid.dimensions): rd = DefaultDimension(name="r%s" % d.name, default_value=self.r) dim_subs.append((d, INT(rd + self.gridpoints[p, i]))) coeffs.append(self.interpolation_coeffs[p, i, rd]) rhs = prod(coeffs) * expr field = field.subs(dim_subs) return [Eq(field, field + rhs.subs(dim_subs))]
def test_flow_detection(self): """Test detection of information flow.""" grid = Grid((10, 10)) u2 = TimeFunction(name="u2", grid=grid, time_order=2) u1 = TimeFunction(name="u1", grid=grid, save=10, time_order=2) exprs = [LoweredEq(indexify(Eq(u1.forward, u1 + 2.0 - u1.backward))), LoweredEq(indexify(Eq(u2.forward, u2 + 2*u2.backward - u1.dt2)))] mapper = detect_flow_directions(exprs) assert mapper.get(grid.stepping_dim) == {Forward} assert mapper.get(grid.time_dim) == {Any, Forward} assert all(mapper.get(i) == {Any} for i in grid.dimensions)
def test_flow_detection(self): """Test detection of information flow.""" grid = Grid((10, 10)) u2 = TimeFunction(name="u2", grid=grid, time_order=2) u1 = TimeFunction(name="u1", grid=grid, save=10, time_order=2) exprs = [LoweredEq(indexify(Eq(u1.forward, u1 + 2.0 - u1.backward))), LoweredEq(indexify(Eq(u2.forward, u2 + 2*u2.backward - u1.dt2)))] mapper = detect_flow_directions(exprs) assert mapper.get(grid.stepping_dim) == {Forward} assert mapper.get(grid.time_dim) == {Any, Forward} assert all(mapper.get(i) == {Any} for i in grid.dimensions)
def callback(): _expr = indexify(expr) _field = indexify(field) p, _ = self.obj.gridpoints.indices dim_subs = [] coeffs = [] for i, d in enumerate(self.obj.grid.dimensions): rd = DefaultDimension(name="r%s" % d.name, default_value=self.r) dim_subs.append((d, INT(rd + self.obj.gridpoints[p, i]))) coeffs.append(self.obj.interpolation_coeffs[p, i, rd]) rhs = prod(coeffs) * _expr _field = _field.subs(dim_subs) return [Eq(_field, _field + rhs.subs(dim_subs))]
def __init__(self, expressions, **kwargs): expressions = as_tuple(expressions) # Input check if any(not isinstance(i, Eq) for i in expressions): raise InvalidOperator("Only `devito.Eq` expressions are allowed.") self.name = kwargs.get("name", "Kernel") subs = kwargs.get("subs", {}) dse = kwargs.get("dse", configuration['dse']) # Header files, etc. self._headers = list(self._default_headers) self._includes = list(self._default_includes) self._globals = list(self._default_globals) # Required for compilation self._compiler = configuration['compiler'] self._lib = None self._cfunction = None # References to local or external routines self._func_table = OrderedDict() # Internal state. May be used to store information about previous runs, # autotuning reports, etc self._state = {} # Expression lowering: indexification, substitution rules, specialization expressions = [indexify(i) for i in expressions] expressions = self._apply_substitutions(expressions, subs) expressions = self._specialize_exprs(expressions) # Expression analysis self.input = filter_sorted(flatten(e.reads for e in expressions)) self.output = filter_sorted(flatten(e.writes for e in expressions)) self.dimensions = filter_sorted( flatten(e.dimensions for e in expressions)) # Group expressions based on their iteration space and data dependences, # and apply the Devito Symbolic Engine (DSE) for flop optimization clusters = clusterize(expressions) clusters = rewrite(clusters, mode=set_dse_mode(dse)) self._dtype, self._dspace = clusters.meta # Lower Clusters to a Schedule tree stree = st_build(clusters) # Lower Schedule tree to an Iteration/Expression tree (IET) iet = iet_build(stree) iet, self._profiler = self._profile_sections(iet) iet = self._specialize_iet(iet, **kwargs) iet = iet_insert_C_decls(iet) iet = self._build_casts(iet) # Derive parameters as symbols not defined in the kernel itself parameters = self._build_parameters(iet) # Finish instantiation super(Operator, self).__init__(self.name, iet, 'int', parameters, ())
def test_ast_convertion(self, equation, expected): """ Test OPS generated expressions for 1, 2 and 3 space dimensions. Parameters ---------- equation : str A string with a :class:`Eq`to be evaluated. expected : str Expected expression to be generated from devito. """ grid_1d = Grid(shape=(4)) grid_2d = Grid(shape=(4, 4)) grid_3d = Grid(shape=(4, 4, 4)) a = 1.43 # noqa b = 0.000000987 # noqa c = 999999999999999 # noqa u = TimeFunction(name='u', grid=grid_1d, space_order=2) # noqa v = TimeFunction(name='v', grid=grid_2d, space_order=2) # noqa w = TimeFunction(name='w', grid=grid_3d, space_order=2) # noqa nfops = OPSNodeFactory() result = make_ops_ast(indexify(eval(equation).evaluate), nfops) assert str(result) == expected
def test_cse(exprs, expected): """Test common subexpressions elimination.""" grid = Grid((3, 3, 3)) dims = grid.dimensions tu = TimeFunction(name="tu", grid=grid, space_order=2) # noqa tv = TimeFunction(name="tv", grid=grid, space_order=2) # noqa tw = TimeFunction(name="tw", grid=grid, space_order=2) # noqa tz = TimeFunction(name="tz", grid=grid, space_order=2) # noqa ti0 = Array(name='ti0', shape=(3, 5, 7), dimensions=dims).indexify() # noqa ti1 = Array(name='ti1', shape=(3, 5, 7), dimensions=dims).indexify() # noqa t0 = Scalar(name='t0') # noqa t1 = Scalar(name='t1') # noqa t2 = Scalar(name='t2') # noqa # List comprehension would need explicit locals/globals mappings to eval for i, e in enumerate(list(exprs)): exprs[i] = DummyEq(indexify(eval(e).evaluate)) counter = generator() make = lambda: Scalar(name='r%d' % counter()).indexify() processed = _cse(exprs, make) assert len(processed) == len(expected) assert all(str(i.rhs) == j for i, j in zip(processed, expected))
def test_ast_convertion(self, equation, expected): """ Test OPS generated expressions for 1, 2 and 3 space dimensions. Parameters ---------- equation : str A string with a :class:`Eq`to be evaluated. expected : str Expected expression to be generated from devito. """ grid_1d = Grid(shape=(4)) grid_2d = Grid(shape=(4, 4)) grid_3d = Grid(shape=(4, 4, 4)) a = 1.43 # noqa b = 0.000000987 # noqa c = 999999999999999 # noqa u = TimeFunction(name='u', grid=grid_1d, space_order=2) # noqa v = TimeFunction(name='v', grid=grid_2d, space_order=2) # noqa w = TimeFunction(name='w', grid=grid_3d, space_order=2) # noqa nfops = OPSNodeFactory() result = make_ops_ast(indexify(eval(equation)), nfops) assert str(result) == expected
def interpolate(self, expr, offset=0, increment=False, self_subs={}): """ Generate equations interpolating an arbitrary expression into ``self``. Parameters ---------- expr : expr-like Input expression to interpolate. offset : int, optional Additional offset from the boundary. increment: bool, optional If True, generate increments (Inc) rather than assignments (Eq). """ expr = indexify(expr) p, _, _ = self.interpolation_coeffs.indices dim_subs = [] coeffs = [] for i, d in enumerate(self.grid.dimensions): rd = DefaultDimension(name="r%s" % d.name, default_value=self.r) dim_subs.append((d, INT(rd + self.gridpoints[p, i]))) coeffs.append(self.interpolation_coeffs[p, i, rd]) # Apply optional time symbol substitutions to lhs of assignment lhs = self.subs(self_subs) rhs = prod(coeffs) * expr.subs(dim_subs) return [Eq(lhs, lhs + rhs)]
def interpolate(self, expr, offset=0, increment=False, self_subs={}): """ Generate equations interpolating an arbitrary expression into ``self``. Parameters ---------- expr : expr-like Input expression to interpolate. offset : int, optional Additional offset from the boundary. increment: bool, optional If True, generate increments (Inc) rather than assignments (Eq). """ expr = indexify(expr) p, _, _ = self.interpolation_coeffs.indices dim_subs = [] coeffs = [] for i, d in enumerate(self.grid.dimensions): rd = DefaultDimension(name="r%s" % d.name, default_value=self.r) dim_subs.append((d, INT(rd + self.gridpoints[p, i]))) coeffs.append(self.interpolation_coeffs[p, i, rd]) # Apply optional time symbol substitutions to lhs of assignment lhs = self.subs(self_subs) rhs = prod(coeffs) * expr.subs(dim_subs) return [Eq(lhs, lhs + rhs)]
def inject(self, field, expr, offset=0, **kwargs): """Symbol for injection of an expression onto a grid :param field: The grid field into which we inject. :param expr: The expression to inject. :param offset: Additional offset from the boundary for absorbing boundary conditions. :param u_t: (Optional) time index to use for indexing into `field`. :param p_t: (Optional) time index to use for indexing into `expr`. """ u_t = kwargs.get('u_t', None) p_t = kwargs.get('p_t', None) expr = indexify(expr) field = indexify(field) variables = list(retrieve_indexed(expr)) + [field] # Apply optional time symbol substitutions to field and expr if u_t is not None: field = field.subs(field.indices[0], u_t) if p_t is not None: expr = expr.subs(self.indices[0], p_t) # List of indirection indices for all adjacent grid points index_matrix = [ tuple(idx + ii + offset for ii, idx in zip(inc, self.coordinate_indices)) for inc in self.point_increments ] # Generate index substituions for all grid variables except # the sparse `SparseFunction` types idx_subs = [] for i, idx in enumerate(index_matrix): v_subs = [(v, v.base[v.indices[:-self.grid.dim] + idx]) for v in variables if not v.base.function.is_SparseFunction] idx_subs += [OrderedDict(v_subs)] # Substitute coordinate base symbols into the coefficients subs = OrderedDict(zip(self.point_symbols, self.coordinate_bases)) return [ Inc(field.subs(vsub), field.subs(vsub) + expr.subs(subs).subs(vsub) * b.subs(subs)) for b, vsub in zip(self.coefficients, idx_subs) ]
def __new__(cls, *args, **kwargs): # Parse input if len(args) == 1: input_expr = args[0] assert type(input_expr) != LoweredEq assert isinstance(input_expr, Eq) elif len(args) == 2: # Reconstructing from existing Eq. E.g., we end up here after xreplace stamp = kwargs.pop('stamp') expr = Eq.__new__(cls, *args, evaluate=False) assert isinstance(stamp, Eq) expr.is_Increment = stamp.is_Increment expr.ispace = stamp.ispace return expr else: raise ValueError("Cannot construct LoweredEq from args=%s " "and kwargs=%s" % (str(args), str(kwargs))) # Indexification expr = indexify(input_expr) # Apply caller-provided substitution subs = kwargs.get('subs') if subs is not None: expr = expr.xreplace(subs) # Well-defined dimension ordering ordering = dimension_sort(expr, key=lambda i: not i.is_Time) # Introduce space sub-dimensions if need to region = getattr(input_expr, '_region', DOMAIN) if region == INTERIOR: mapper = { i: SubDimension("%si" % i, i, 1, -1) for i in ordering if i.is_Space } expr = expr.xreplace(mapper) ordering = [mapper.get(i, i) for i in ordering] # Compute iteration space intervals, iterators = compute_intervals(expr) intervals = sorted(intervals, key=lambda i: ordering.index(i.dim)) directions, _ = compute_directions(expr, lambda i: Any) ispace = IterationSpace([i.negate() for i in intervals], iterators, directions) # 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.ispace = ispace expr.dimensions = ordering expr.reads, expr.writes = detect_io(expr) return expr
def test_fd_indices(self, so): """ Test that shifted derivative have Integer offset after indexification. """ grid = Grid((10, )) x = grid.dimensions[0] x0 = x + .5 * x.spacing u = Function(name="u", grid=grid, space_order=so) dx = indexify(u.dx(x0=x0).evaluate) for f in retrieve_indexed(dx): assert len(f.indices[0].atoms(Float)) == 0
def test_index_shifting(self, expr, so, to, expected): """Tests that array accesses get properly shifted based on the halo and padding regions extent.""" grid = Grid(shape=(4, 4, 4)) x, y, z = grid.dimensions t = grid.stepping_dim # noqa u = TimeFunction(name='u', grid=grid, space_order=so, time_order=to) # noqa m = Function(name='m', grid=grid, space_order=0) # noqa expr = eval(expr) expr = Operator(expr)._specialize_exprs([indexify(expr)])[0] assert str(expr).replace(' ', '') == expected
def guard(self, expr=None, offset=0): """ Generate guarded expressions, that is expressions that are evaluated by an Operator only if certain conditions are met. The introduced condition, here, is that all grid points in the support of a sparse value must fall within the grid domain (i.e., *not* on the halo). Parameters ---------- expr : expr-like, optional Input expression, from which the guarded expression is derived. If not specified, defaults to ``self``. offset : int, optional Relax the guard condition by introducing a tolerance offset. """ _, points = self._index_matrix(offset) # Guard through ConditionalDimension conditions = {} for d, idx in zip(self.grid.dimensions, self._coordinate_indices): p = points[idx] lb = sympy.And(p >= d.symbolic_min - offset, evaluate=False) ub = sympy.And(p <= d.symbolic_max + offset, evaluate=False) conditions[p] = sympy.And(lb, ub, evaluate=False) condition = sympy.And(*conditions.values(), evaluate=False) cd = ConditionalDimension("%s_g" % self._sparse_dim, self._sparse_dim, condition=condition) if expr is None: out = self.indexify().xreplace({self._sparse_dim: cd}) else: functions = { f for f in retrieve_function_carriers(expr) if f.is_SparseFunction } out = indexify(expr).xreplace( {f._sparse_dim: cd for f in functions}) # Temporaries for the position temps = [ Eq(v, k, implicit_dims=self.dimensions) for k, v in self._position_map.items() ] # Temporaries for the indirection dimensions temps.extend([ Eq(v, k.subs(self._position_map), implicit_dims=self.dimensions) for k, v in points.items() if v in conditions ]) return out, temps
def interpolate(self, expr, offset=0, u_t=None, p_t=None, cummulative=False): """Creates a :class:`sympy.Eq` equation for the interpolation of an expression onto this sparse point collection. :param expr: The expression to interpolate. :param offset: Additional offset from the boundary for absorbing boundary conditions. :param u_t: (Optional) time index to use for indexing into field data in `expr`. :param p_t: (Optional) time index to use for indexing into the sparse point data. :param cummulative: (Optional) If True, perform an increment rather than an assignment. Defaults to False. """ expr = indexify(expr) # Apply optional time symbol substitutions to expr if u_t is not None: time = self.grid.time_dim t = self.grid.stepping_dim expr = expr.subs(t, u_t).subs(time, u_t) variables = list(retrieve_indexed(expr)) # List of indirection indices for all adjacent grid points index_matrix = [ tuple(idx + ii + offset for ii, idx in zip(inc, self.coordinate_indices)) for inc in self.point_increments ] # Generate index substituions for all grid variables idx_subs = [] for i, idx in enumerate(index_matrix): v_subs = [(v, v.base[v.indices[:-self.grid.dim] + idx]) for v in variables] idx_subs += [OrderedDict(v_subs)] # Substitute coordinate base symbols into the coefficients subs = OrderedDict(zip(self.point_symbols, self.coordinate_bases)) rhs = sum([ expr.subs(vsub) * b.subs(subs) for b, vsub in zip(self.coefficients, idx_subs) ]) # Apply optional time symbol substitutions to lhs of assignment lhs = self if p_t is None else self.subs(self.indices[0], p_t) rhs = rhs + lhs if cummulative is True else rhs return [Eq(lhs, rhs)]
def callback(): _expr = indexify(expr) p, _, _ = self.obj.interpolation_coeffs.indices dim_subs = [] coeffs = [] for i, d in enumerate(self.obj.grid.dimensions): rd = DefaultDimension(name="r%s" % d.name, default_value=self.r) dim_subs.append((d, INT(rd + self.obj.gridpoints[p, i]))) coeffs.append(self.obj.interpolation_coeffs[p, i, rd]) # Apply optional time symbol substitutions to lhs of assignment lhs = self.obj.subs(self_subs) rhs = prod(coeffs) * _expr.subs(dim_subs) return [Eq(lhs, lhs + rhs)]
def test_time_subsampling_fd(self): nt = 19 grid = Grid(shape=(11, 11)) x, y = grid.dimensions time = grid.time_dim factor = 4 time_subsampled = ConditionalDimension('t_sub', parent=time, factor=factor) usave = TimeFunction(name='usave', grid=grid, save=(nt+factor-1)//factor, time_dim=time_subsampled, time_order=2) dx2 = [indexify(i) for i in retrieve_functions(usave.dt2.evaluate)] assert dx2 == [usave[time_subsampled - 1, x, y], usave[time_subsampled + 1, x, y], usave[time_subsampled, x, y]]
def test_accesses_extraction(self, equation, expected): grid_1d = Grid(shape=(4)) grid_3d = Grid(shape=(4, 4, 4)) a = 1.43 # noqa c = 999999999999999 # noqa u = TimeFunction(name='u', grid=grid_1d, space_order=2) # noqa v = TimeFunction(name='v', grid=grid_1d, space_order=2) # noqa w = TimeFunction(name='w', grid=grid_3d, space_order=2) # noqa node_factory = OPSNodeFactory() make_ops_ast(indexify(eval(equation).evaluate), node_factory) result = eval(expected) for k, v in node_factory.ops_args_accesses.items(): assert len(v) == len(result[k.name]) for idx in result[k.name]: assert idx in v
def test_tti_clusters_to_graph(): solver = tti_operator() expressions = solver.op_fwd('centered').args['expressions'] subs = solver.op_fwd('centered').args['subs'] expressions = [indexify(s) for s in expressions] expressions = [s.xreplace(subs) for s in expressions] stencils = make_stencils(expressions) clusters = clusterize(expressions, stencils) assert len(clusters) == 3 main_cluster = clusters[0] n_output_tensors = len(main_cluster.trace) clusters = rewrite([main_cluster], mode='basic') assert len(clusters) == 1 main_cluster = clusters[0] graph = main_cluster.trace assert len([v for v in graph.values() if v.is_tensor]) == n_output_tensors # u and v assert all(v.reads or v.readby for v in graph.values())
def _lower_exprs(cls, expressions, **kwargs): """ Expression lowering: * Form and gather any required implicit expressions; * Evaluate derivatives; * Flatten vectorial equations; * Indexify Functions; * Apply substitution rules; * Specialize (e.g., index shifting) """ subs = kwargs.get("subs", {}) expressions = cls._add_implicit(expressions) expressions = [i.evaluate for i in expressions] expressions = [j for i in expressions for j in i._flatten] expressions = [indexify(i) for i in expressions] expressions = cls._apply_substitutions(expressions, subs) expressions = cls._specialize_exprs(expressions) return expressions
def guard(self, expr=None, offset=0): """ Generate guarded expressions, that is expressions that are evaluated by an Operator only if certain conditions are met. The introduced condition, here, is that all grid points in the support of a sparse value must fall within the grid domain (i.e., *not* on the halo). Parameters ---------- expr : expr-like, optional Input expression, from which the guarded expression is derived. If not specified, defaults to ``self``. offset : int, optional Relax the guard condition by introducing a tolerance offset. """ _, points = self._index_matrix(offset) # Guard through ConditionalDimension conditions = {} for d, idx in zip(self.grid.dimensions, self._coordinate_indices): p = points[idx] lb = sympy.And(p >= d.symbolic_min - offset, evaluate=False) ub = sympy.And(p <= d.symbolic_max + offset, evaluate=False) conditions[p] = sympy.And(lb, ub, evaluate=False) condition = sympy.And(*conditions.values(), evaluate=False) cd = ConditionalDimension("%s_g" % self._sparse_dim, self._sparse_dim, condition=condition) if expr is None: out = self.indexify().xreplace({self._sparse_dim: cd}) else: functions = {f for f in retrieve_function_carriers(expr) if f.is_SparseFunction} out = indexify(expr).xreplace({f._sparse_dim: cd for f in functions}) # Temporaries for the indirection dimensions temps = [Eq(v, k, implicit_dims=self.dimensions) for k, v in points.items() if v in conditions] return out, temps
def __new__(cls, input_expr, subs=None): # Sanity check assert type(input_expr) != LoweredEq assert isinstance(input_expr, Eq) # Indexification expr = indexify(input_expr) # Apply caller-provided substitution if subs is not None: expr = expr.xreplace(subs) expr = super(LoweredEq, cls).__new__(cls, expr.lhs, expr.rhs, evaluate=False) expr.is_Increment = getattr(input_expr, 'is_Increment', False) # Get the accessed data points stencil = Stencil(expr) # Well-defined dimension ordering ordering = dimension_sort(expr, key=lambda i: not i.is_Time) # Split actual Intervals (the data spaces) from the "derived" iterators, # to build an IterationSpace iterators = OrderedDict() for i in ordering: if i.is_Derived: iterators.setdefault(i.parent, []).append(stencil.entry(i)) else: iterators.setdefault(i, []) intervals = [] for k, v in iterators.items(): offs = set.union(set(stencil.get(k)), *[i.ofs for i in v]) intervals.append(Interval(k, min(offs), max(offs)).negate()) expr.ispace = IterationSpace(intervals, iterators) return expr
def __new__(cls, *args, **kwargs): # Parse input if len(args) == 1: input_expr = args[0] assert type(input_expr) != LoweredEq assert isinstance(input_expr, Eq) elif len(args) == 2: # Reconstructing from existing Eq. E.g., we end up here after xreplace expr = super(Eq, cls).__new__(cls, *args, evaluate=False) stamp = kwargs.get('stamp') assert isinstance(stamp, Eq) expr.is_Increment = stamp.is_Increment expr.dspace = stamp.dspace expr.ispace = stamp.ispace return expr else: raise ValueError("Cannot construct Eq from args=%s " "and kwargs=%s" % (str(args), str(kwargs))) # Indexification expr = indexify(input_expr) # Apply caller-provided substitution subs = kwargs.get('subs') if subs is not None: expr = expr.xreplace(subs) # Well-defined dimension ordering ordering = dimension_sort(expr, key=lambda i: not i.is_Time) # Introduce space sub-dimensions if need to region = getattr(input_expr, '_region', DOMAIN) if region == INTERIOR: mapper = { i: SubDimension("%si" % i, i, 1, -1) for i in ordering if i.is_Space } expr = expr.xreplace(mapper) ordering = [mapper.get(i, i) for i in ordering] # Get the accessed data points stencil = Stencil(expr) # Split actual Intervals (the data spaces) from the "derived" iterators, # to build an IterationSpace iterators = OrderedDict() for i in ordering: if i.is_Stepping: iterators.setdefault(i.parent, []).append(stencil.entry(i)) else: iterators.setdefault(i, []) intervals = [] for k, v in iterators.items(): offs = set.union(set(stencil.get(k)), *[i.ofs for i in v]) intervals.append(Interval(k, min(offs), max(offs))) # 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 = DataSpace(intervals) expr.ispace = IterationSpace([i.negate() for i in intervals], iterators) return expr
def __init__(self, expressions, **kwargs): expressions = as_tuple(expressions) # Input check if any(not isinstance(i, Eq) for i in expressions): raise InvalidOperator("Only `devito.Eq` expressions are allowed.") self.name = kwargs.get("name", "Kernel") subs = kwargs.get("subs", {}) dse = kwargs.get("dse", configuration['dse']) # Header files, etc. self._headers = list(self._default_headers) self._includes = list(self._default_includes) self._globals = list(self._default_globals) # Required for compilation self._compiler = configuration['compiler'] self._lib = None self._cfunction = None # References to local or external routines self._func_table = OrderedDict() # Internal state. May be used to store information about previous runs, # autotuning reports, etc self._state = {} # Form and gather any required implicit expressions expressions = self._add_implicit(expressions) # Expression lowering: indexification, substitution rules, specialization expressions = [indexify(i) for i in expressions] expressions = self._apply_substitutions(expressions, subs) expressions = self._specialize_exprs(expressions) # Expression analysis self._input = filter_sorted(flatten(e.reads + e.writes for e in expressions)) self._output = filter_sorted(flatten(e.writes for e in expressions)) self._dimensions = filter_sorted(flatten(e.dimensions for e in expressions)) # Group expressions based on their iteration space and data dependences, # and apply the Devito Symbolic Engine (DSE) for flop optimization clusters = clusterize(expressions) clusters = rewrite(clusters, mode=set_dse_mode(dse)) self._dtype, self._dspace = clusters.meta # Lower Clusters to a Schedule tree stree = st_build(clusters) # Lower Schedule tree to an Iteration/Expression tree (IET) iet = iet_build(stree) iet, self._profiler = self._profile_sections(iet) iet = self._specialize_iet(iet, **kwargs) # Derive all Operator parameters based on the IET parameters = derive_parameters(iet, True) # Finalization: introduce declarations, type casts, etc iet = self._finalize(iet, parameters) super(Operator, self).__init__(self.name, iet, 'int', parameters, ())
def __init__(self, expressions, **kwargs): expressions = as_tuple(expressions) # Input check if any(not isinstance(i, Eq) for i in expressions): raise InvalidOperator("Only `devito.Eq` expressions are allowed.") self.name = kwargs.get("name", "Kernel") subs = kwargs.get("subs", {}) dse = kwargs.get("dse", configuration['dse']) # Header files, etc. self._headers = list(self._default_headers) self._includes = list(self._default_includes) self._globals = list(self._default_globals) # Required for compilation self._compiler = configuration['compiler'] self._lib = None self._cfunction = None # References to local or external routines self._func_table = OrderedDict() # Internal state. May be used to store information about previous runs, # autotuning reports, etc self._state = self._initialize_state(**kwargs) # Form and gather any required implicit expressions expressions = self._add_implicit(expressions) # Expression lowering: evaluation of derivatives, indexification, # substitution rules, specialization expressions = [i.evaluate for i in expressions] expressions = [indexify(i) for i in expressions] expressions = self._apply_substitutions(expressions, subs) expressions = self._specialize_exprs(expressions) # Expression analysis self._input = filter_sorted( flatten(e.reads + e.writes for e in expressions)) self._output = filter_sorted(flatten(e.writes for e in expressions)) self._dimensions = filter_sorted( flatten(e.dimensions for e in expressions)) # Group expressions based on their iteration space and data dependences # Several optimizations are applied (fusion, lifting, flop reduction via DSE, ...) clusters = clusterize(expressions, dse_mode=set_dse_mode(dse)) self._dtype, self._dspace = clusters.meta # Lower Clusters to a Schedule tree stree = st_build(clusters) # Lower Schedule tree to an Iteration/Expression tree (IET) iet = iet_build(stree) iet, self._profiler = self._profile_sections(iet) iet = self._specialize_iet(iet, **kwargs) # Derive all Operator parameters based on the IET parameters = derive_parameters(iet, True) # Finalization: introduce declarations, type casts, etc iet = self._finalize(iet, parameters) super(Operator, self).__init__(self.name, iet, 'int', parameters, ())
def __init__(self, expressions, **kwargs): expressions = as_tuple(expressions) # Input check if any(not isinstance(i, sympy.Eq) for i in expressions): raise InvalidOperator("Only SymPy expressions are allowed.") self.name = kwargs.get("name", "Kernel") subs = kwargs.get("subs", {}) dse = kwargs.get("dse", configuration['dse']) # Header files, etc. self._headers = list(self._default_headers) self._includes = list(self._default_includes) self._globals = list(self._default_globals) # Required for compilation self._compiler = configuration['compiler'] self._lib = None self._cfunction = None # References to local or external routines self.func_table = OrderedDict() # Expression lowering: indexification, substitution rules, specialization expressions = [indexify(i) for i in expressions] expressions = [i.xreplace(subs) for i in expressions] expressions = self._specialize_exprs(expressions) # Expression analysis self.input = filter_sorted(flatten(e.reads for e in expressions)) self.output = filter_sorted(flatten(e.writes for e in expressions)) self.dimensions = filter_sorted(flatten(e.dimensions for e in expressions)) # Group expressions based on their iteration space and data dependences, # and apply the Devito Symbolic Engine (DSE) for flop optimization clusters = clusterize(expressions) clusters = rewrite(clusters, mode=set_dse_mode(dse)) self._dtype, self._dspace = clusters.meta # Lower Clusters to a Schedule tree stree = schedule(clusters) stree = section(stree) # Lower Sections to an Iteration/Expression tree (IET) iet = iet_build(stree) # Insert code for C-level performance profiling iet, self.profiler = self._profile_sections(iet) # Translate into backend-specific representation iet = self._specialize_iet(iet, **kwargs) # Insert the required symbol declarations iet = iet_insert_C_decls(iet, self.func_table) # Insert data and pointer casts for array parameters and profiling structs iet = self._build_casts(iet) # Derive parameters as symbols not defined in the kernel itself parameters = self._build_parameters(iet) # Finish instantiation super(Operator, self).__init__(self.name, iet, 'int', parameters, ())
def __init__(self, expressions, **kwargs): expressions = as_tuple(expressions) # Input check if any(not isinstance(i, sympy.Eq) for i in expressions): raise InvalidOperator("Only SymPy expressions are allowed.") self.name = kwargs.get("name", "Kernel") subs = kwargs.get("subs", {}) time_axis = kwargs.get("time_axis", Forward) dse = kwargs.get("dse", configuration['dse']) dle = kwargs.get("dle", configuration['dle']) # Header files, etc. self._headers = list(self._default_headers) self._includes = list(self._default_includes) self._globals = list(self._default_globals) # Required for compilation self._compiler = configuration['compiler'] self._lib = None self._cfunction = None # References to local or external routines self.func_table = OrderedDict() # Expression lowering expressions = [indexify(s) for s in expressions] expressions = [s.xreplace(subs) for s in expressions] # Analysis self.dtype = retrieve_dtype(expressions) self.input, self.output, self.dimensions = retrieve_symbols( expressions) stencils = make_stencils(expressions) self.offsets = { d.end_name: v for d, v in retrieve_offsets(stencils).items() } # Set the direction of time acoording to the given TimeAxis for time in [d for d in self.dimensions if d.is_Time]: if not time.is_Stepping: time.reverse = time_axis == Backward # Parameters of the Operator (Dimensions necessary for data casts) parameters = self.input + self.dimensions # Group expressions based on their Stencil and data dependences clusters = clusterize(expressions, stencils) # Apply the Devito Symbolic Engine (DSE) for symbolic optimization clusters = rewrite(clusters, mode=set_dse_mode(dse)) # Wrap expressions with Iterations according to dimensions nodes = self._schedule_expressions(clusters) # Data dependency analysis. Properties are attached directly to nodes nodes = analyze_iterations(nodes) # Introduce C-level profiling infrastructure nodes, self.profiler = self._profile_sections(nodes, parameters) # Resolve and substitute dimensions for loop index variables nodes, subs = ResolveTimeStepping().visit(nodes) nodes = SubstituteExpression(subs=subs).visit(nodes) # Translate into backend-specific representation (e.g., GPU, Yask) nodes = self._specialize(nodes, parameters) # Apply the Devito Loop Engine (DLE) for loop optimization dle_state = transform(nodes, *set_dle_mode(dle)) # Update the Operator state based on the DLE self.dle_arguments = dle_state.arguments self.dle_flags = dle_state.flags self.func_table.update( OrderedDict([(i.name, FunMeta(i, True)) for i in dle_state.elemental_functions])) parameters.extend([i.argument for i in self.dle_arguments]) self.dimensions.extend([ i.argument for i in self.dle_arguments if isinstance(i.argument, Dimension) ]) self._includes.extend(list(dle_state.includes)) # Introduce all required C declarations nodes = self._insert_declarations(dle_state.nodes) # Finish instantiation super(Operator, self).__init__(self.name, nodes, 'int', parameters, ())
def __init__(self, expressions, **kwargs): expressions = as_tuple(expressions) # Input check if any(not isinstance(i, sympy.Eq) for i in expressions): raise InvalidOperator("Only SymPy expressions are allowed.") self.name = kwargs.get("name", "Kernel") subs = kwargs.get("subs", {}) dse = kwargs.get("dse", configuration['dse']) dle = kwargs.get("dle", configuration['dle']) # Header files, etc. self._headers = list(self._default_headers) self._includes = list(self._default_includes) self._globals = list(self._default_globals) # Required for compilation self._compiler = configuration['compiler'] self._lib = None self._cfunction = None # References to local or external routines self.func_table = OrderedDict() # Expression lowering: indexification, substitution rules, specialization expressions = [indexify(i) for i in expressions] expressions = [i.xreplace(subs) for i in expressions] expressions = self._specialize_exprs(expressions) # Expression analysis self.input = filter_sorted(flatten(e.reads for e in expressions)) self.output = filter_sorted(flatten(e.writes for e in expressions)) self.dimensions = filter_sorted(flatten(e.dimensions for e in expressions)) # Group expressions based on their iteration space and data dependences, # and apply the Devito Symbolic Engine (DSE) for flop optimization clusters = clusterize(expressions) clusters = rewrite(clusters, mode=set_dse_mode(dse)) self._dtype, self._dspace = clusters.meta # Lower Clusters to an Iteration/Expression tree (IET) nodes = iet_build(clusters) # Introduce C-level profiling infrastructure nodes, self.profiler = self._profile_sections(nodes) # Translate into backend-specific representation (e.g., GPU, Yask) nodes = self._specialize_iet(nodes) # Apply the Devito Loop Engine (DLE) for loop optimization dle_state = transform(nodes, *set_dle_mode(dle)) # Update the Operator state based on the DLE self.dle_args = dle_state.arguments self.dle_flags = dle_state.flags self.func_table.update(OrderedDict([(i.name, MetaCall(i, True)) for i in dle_state.elemental_functions])) self.dimensions.extend([i.argument for i in self.dle_args if isinstance(i.argument, Dimension)]) self._includes.extend(list(dle_state.includes)) # Introduce the required symbol declarations nodes = iet_insert_C_decls(dle_state.nodes, self.func_table) # Insert data and pointer casts for array parameters and profiling structs nodes = self._build_casts(nodes) # Derive parameters as symbols not defined in the kernel itself parameters = self._build_parameters(nodes) # Finish instantiation super(Operator, self).__init__(self.name, nodes, 'int', parameters, ())