Ejemplo n.º 1
0
def create_ops_arg(p, name_to_ops_dat, par_to_ops_stencil):
    if p.is_Constant:
        return namespace['ops_arg_gbl'](
            Byref(Constant(name=p.name[1:])), 1,
            Literal('"%s"' % dtype_to_cstr(p.dtype)), namespace['ops_read'])
    else:
        return namespace['ops_arg_dat'](
            name_to_ops_dat[p.name], 1, par_to_ops_stencil[p],
            Literal('"%s"' % dtype_to_cstr(p.dtype)),
            namespace['ops_read'] if p.read_only else namespace['ops_write'])
Ejemplo n.º 2
0
def create_ops_arg(p, accessible_origin, name_to_ops_dat, par_to_ops_stencil):
    elements_per_point = 1
    dtype = Literal('"%s"' % dtype_to_cstr(p.dtype))

    if p.is_Constant:
        ops_type = namespace['ops_arg_gbl']
        ops_name = Byref(Constant(name=p.name[1:]))
        rw_flag = namespace['ops_read']
    else:
        ops_type = namespace['ops_arg_dat']
        accessible_info = accessible_origin[p.name]
        ops_name = name_to_ops_dat[p.name] \
            if accessible_info.time is None \
            else name_to_ops_dat[accessible_info.origin_name].\
            indexify([Add(accessible_info.time, accessible_info.shift)])
        rw_flag = namespace['ops_read'] if p.read_only else namespace[
            'ops_write']

    ops_arg = OpsArgDecl(ops_type=ops_type,
                         ops_name=ops_name,
                         elements_per_point=elements_per_point,
                         dtype=dtype,
                         rw_flag=rw_flag)

    return ops_arg
Ejemplo n.º 3
0
 def __repr__(self):
     parameters = ",".join([
         'void*' if i.is_Object else dtype_to_cstr(i.dtype)
         for i in self.parameters
     ])
     return "%s[%s]<%s; %s>" % (self.__class__.__name__, self.name,
                                self.retval, parameters)
Ejemplo n.º 4
0
def create_ops_arg(p, accessible_origin, name_to_ops_dat, par_to_ops_stencil):
    if p.is_Constant:
        return namespace['ops_arg_gbl'](
            Byref(Constant(name=p.name[1:])), 1,
            Literal('"%s"' % dtype_to_cstr(p.dtype)), namespace['ops_read'])
    else:
        accessible_info = accessible_origin[p.name]

        dat_name = name_to_ops_dat[p.name] \
            if accessible_info.time is None \
            else name_to_ops_dat[accessible_info.origin_name].\
            indexify([accessible_info.time])

        return namespace['ops_arg_dat'](
            dat_name, 1, par_to_ops_stencil[p],
            Literal('"%s"' % dtype_to_cstr(p.dtype)),
            namespace['ops_read'] if p.read_only else namespace['ops_write'])
Ejemplo n.º 5
0
def get_ops_args(args, stencils, name_to_dat):
    ops_args = []

    for arg in args:
        if arg.is_Constant:
            ops_args.append(
                Call("ops_arg_gbl", [
                    Byref(Constant(name=arg.name[1:])), 1,
                    String(dtype_to_cstr(arg.dtype)), OPS_READ
                ], False))
        else:
            ops_args.append(
                Call("ops_arg_dat", [
                    name_to_dat[arg.name], 1, stencils[arg.name],
                    String(dtype_to_cstr(arg.dtype)),
                    OPS_WRITE if arg.is_Write else OPS_READ
                ], False))

    return ops_args
Ejemplo n.º 6
0
    def test_create_ops_arg_constant(self):
        a = Constant(name='*a')

        ops_arg = create_ops_arg(a, {}, {}, {})

        assert ops_arg.ops_type == namespace['ops_arg_gbl']
        assert str(ops_arg.ops_name) == str(Byref(Constant(name='a')))
        assert ops_arg.elements_per_point == 1
        assert ops_arg.dtype == Literal('"%s"' % dtype_to_cstr(a.dtype))
        assert ops_arg.rw_flag == namespace['ops_read']
Ejemplo n.º 7
0
    def test_create_ops_arg_constant(self):
        a = Constant(name='*a')

        res = create_ops_arg(a, {}, {}, {})

        assert type(res) == namespace['ops_arg_gbl']
        assert str(res.args[0]) == str(Byref(Constant(name='a')))
        assert res.args[1] == 1
        assert res.args[2] == Literal('"%s"' % dtype_to_cstr(a.dtype))
        assert res.args[3] == namespace['ops_read']
Ejemplo n.º 8
0
        def test_create_ops_arg_constant(self):
            a = Constant(name='*a')

            res = create_ops_arg(a, {}, {})

            assert res.name == namespace['ops_arg_gbl'].name
            assert res.args == [
                Byref(Constant(name='a')), 1,
                Literal('"%s"' % dtype_to_cstr(a.dtype)), namespace['ops_read']
            ]
Ejemplo n.º 9
0
    def test_create_ops_arg_function(self, read):

        u = OpsAccessible('u', np.float32, read)
        dat = OpsDat('u_dat')
        stencil = OpsStencil('stencil')
        info = AccessibleInfo(u, None, None)

        res = create_ops_arg(u, {'u': info}, {'u': dat}, {u: stencil})

        assert type(res) == namespace['ops_arg_dat']
        assert res.args == (dat, 1, stencil,
                            Literal('"%s"' % dtype_to_cstr(u.dtype)),
                            namespace['ops_read']
                            if read else namespace['ops_write'])
Ejemplo n.º 10
0
    def test_create_ops_arg_function(self, read):

        u = OpsAccessible('u', dtype=np.float32, read_only=read)
        dat = OpsDat('u_dat')
        stencil = OpsStencil('stencil')
        info = AccessibleInfo(u, None, None)

        ops_arg = create_ops_arg(u, {'u': info}, {'u': dat}, {u: stencil})

        assert ops_arg.ops_type == namespace['ops_arg_dat']
        assert ops_arg.ops_name == OpsDat('u_dat')
        assert ops_arg.elements_per_point == 1
        assert ops_arg.dtype == Literal('"%s"' % dtype_to_cstr(u.dtype))
        assert ops_arg.rw_flag == \
            namespace['ops_read'] if read else namespace['ops_write']
Ejemplo n.º 11
0
    def _C_typename(self):
        if isinstance(self.dtype, str):
            return self.dtype

        return dtype_to_cstr(self.dtype)
Ejemplo n.º 12
0
 def _C_typedata(self):
     return dtype_to_cstr(self.dtype)
Ejemplo n.º 13
0
 def visit_LocalExpression(self, o):
     return c.Initializer(
         c.Value(dtype_to_cstr(o.dtype), ccode(o.expr.lhs, dtype=o.dtype)),
         ccode(o.expr.rhs, dtype=o.dtype))
Ejemplo n.º 14
0
class Dimension(ArgProvider):
    """
    Symbol defining an iteration space.

    A Dimension represents a problem dimension. It is typically used to index
    into Functions, but it can also appear in the middle of a symbolic expression
    just like any other symbol.

    Dimension is the root of a hierarchy of classes, which looks as follows (only
    the classes exposed to the level of the user API are shown)::

                                       Dimension
                                           |
                              ---------------------------
                              |                         |
                       BasicDimension            DefaultDimension
                              |
                      DerivedDimension
                              |
            ---------------------------------------
            |                 |                   |
      SteppingDimension   SubDimension   ConditionalDimension

    Parameters
    ----------
    name : str
        Name of the dimension.
    spacing : symbol, optional
        A symbol to represent the physical spacing along this Dimension.

    Examples
    --------
    Dimensions are automatically created when a Grid is instantiated.

    >>> from devito import Grid
    >>> grid = Grid(shape=(4, 4))
    >>> x, y = grid.dimensions
    >>> type(x)
    <class 'devito.types.dimension.SpaceDimension'>
    >>> time = grid.time_dim
    >>> type(time)
    <class 'devito.types.dimension.TimeDimension'>
    >>> t = grid.stepping_dim
    >>> type(t)
    <class 'devito.types.dimension.SteppingDimension'>

    Alternatively, one can create Dimensions explicitly

    >>> from devito import Dimension
    >>> i = Dimension(name='i')

    Or, when many "free" Dimensions are needed, with the shortcut

    >>> from devito import dimensions
    >>> i, j, k = dimensions('i j k')

    A Dimension can be used to build a Function as well as within symbolic
    expressions, as both array index ("indexed notation") and free symbol.

    >>> from devito import Function
    >>> f = Function(name='f', shape=(4, 4), dimensions=(i, j))
    >>> f + f
    2*f(i, j)
    >>> f[i + 1, j] + f[i, j + 1]
    f[i, j + 1] + f[i + 1, j]
    >>> f*i
    i*f(i, j)
    """

    is_Dimension = True
    is_Space = False
    is_Time = False

    is_Default = False
    is_Custom = False
    is_Derived = False
    is_NonlinearDerived = False
    is_Sub = False
    is_Conditional = False
    is_Stepping = False
    is_Modulo = False
    is_Incr = False
    is_Shifted = False

    _C_typename = 'const %s' % dtype_to_cstr(np.int32)
    _C_typedata = _C_typename

    def __new__(cls, *args, **kwargs):
        """
        Equivalent to ``BasicDimension(*args, **kwargs)``.

        Notes
        -----
        This is only necessary for backwards compatibility, as originally
        there was no BasicDimension (i.e., Dimension was just the top class).
        """
        if cls is Dimension:
            return BasicDimension(*args, **kwargs)
        else:
            return BasicDimension.__new__(cls, *args, **kwargs)

    @classmethod
    def __dtype_setup__(cls, **kwargs):
        # Unlike other Symbols, Dimensions can only be integers
        return np.int32

    def __str__(self):
        return self.name

    @property
    def spacing(self):
        """Symbol representing the physical spacing along the Dimension."""
        return self._spacing

    @cached_property
    def symbolic_size(self):
        """Symbolic size of the Dimension."""
        return Scalar(name=self.size_name, dtype=np.int32, is_const=True)

    @cached_property
    def symbolic_min(self):
        """Symbol defining the minimum point of the Dimension."""
        return Scalar(name=self.min_name, dtype=np.int32, is_const=True)

    @cached_property
    def symbolic_max(self):
        """Symbol defining the maximum point of the Dimension."""
        return Scalar(name=self.max_name, dtype=np.int32, is_const=True)

    @property
    def symbolic_incr(self):
        """The increment value while iterating over the Dimension."""
        return sympy.S.One

    @cached_property
    def size_name(self):
        return "%s_size" % self.name

    @cached_property
    def min_name(self):
        return "%s_m" % self.name

    @cached_property
    def max_name(self):
        return "%s_M" % self.name

    @property
    def is_const(self):
        return False

    @property
    def root(self):
        return self

    @property
    def _maybe_distributed(self):
        """Could it be a distributed Dimension?"""
        return True

    @property
    def _C_name(self):
        return self.name

    @cached_property
    def _defines(self):
        return frozenset({self})

    @cached_property
    def _defines_symbols(self):
        candidates = [
            self.symbolic_min, self.symbolic_max, self.symbolic_size,
            self.symbolic_incr
        ]
        return frozenset(i for i in candidates if not i.is_Number)

    @property
    def _arg_names(self):
        """Tuple of argument names introduced by the Dimension."""
        return (self.name, self.size_name, self.max_name, self.min_name)

    @memoized_meth
    def _arg_defaults(self, _min=None, size=None, alias=None):
        """
        A map of default argument values defined by the Dimension.

        Parameters
        ----------
        _min : int, optional
            Minimum point as provided by data-carrying objects.
        size : int, optional
            Size as provided by data-carrying symbols.
        alias : Dimension, optional
            To get the min/max/size names under which to store values. Use
            self's if None.
        """
        dim = alias or self
        return {
            dim.min_name: _min or 0,
            dim.size_name: size,
            dim.max_name: size if size is None else size - 1
        }

    def _arg_values(self, args, interval, grid, **kwargs):
        """
        Produce a map of argument values after evaluating user input. If no user
        input is provided, get a known value in ``args`` and adjust it so that no
        out-of-bounds memory accesses will be performed. The adjustment exploits
        the information in ``interval``, an Interval describing the Dimension data
        space. If no value is available in ``args``, use a default value.

        Parameters
        ----------
        args : dict
            Known argument values.
        interval : Interval
            Description of the Dimension data space.
        grid : Grid
            Used for spacing overriding and MPI execution; if ``self`` is a distributed
            Dimension, then ``grid`` is used to translate user input into rank-local
            indices.
        **kwargs
            Dictionary of user-provided argument overrides.
        """
        # Fetch user input and convert into rank-local values
        glb_minv = kwargs.pop(self.min_name, None)
        glb_maxv = kwargs.pop(self.max_name, kwargs.pop(self.name, None))
        if grid is not None and grid.is_distributed(self):
            loc_minv, loc_maxv = grid.distributor.glb_to_loc(
                self, (glb_minv, glb_maxv))
        else:
            loc_minv, loc_maxv = glb_minv, glb_maxv

        # If no user-override provided, use a suitable default value
        defaults = self._arg_defaults()
        if glb_minv is None:
            loc_minv = args.get(self.min_name, defaults[self.min_name])
            try:
                loc_minv -= min(interval.lower, 0)
            except (AttributeError, TypeError):
                pass
        if glb_maxv is None:
            loc_maxv = args.get(self.max_name, defaults[self.max_name])
            try:
                loc_maxv -= max(interval.upper, 0)
            except (AttributeError, TypeError):
                pass

        args = {self.min_name: loc_minv, self.max_name: loc_maxv}

        # Maybe override spacing
        if grid is not None:
            try:
                spacing_map = {k.name: v for k, v in grid.spacing_map.items()}
                args[self.spacing.name] = spacing_map[self.spacing.name]
            except KeyError:
                pass
            except AttributeError:
                # See issue #1524
                warning("Unable to override spacing")

        return args

    def _arg_check(self, args, size, interval):
        """
        Raises
        ------
        InvalidArgument
            If any of the ``self``-related runtime arguments in ``args``
            will cause an out-of-bounds access.
        """
        if self.min_name not in args:
            raise InvalidArgument("No runtime value for %s" % self.min_name)
        if interval.is_Defined and args[self.min_name] + interval.lower < 0:
            raise InvalidArgument("OOB detected due to %s=%d" %
                                  (self.min_name, args[self.min_name]))

        if self.max_name not in args:
            raise InvalidArgument("No runtime value for %s" % self.max_name)
        if interval.is_Defined:
            if is_integer(interval.upper):
                upper = interval.upper
            else:
                # Autopadding causes non-integer upper limit
                upper = interval.upper.subs(args)
            if args[self.max_name] + upper >= size:
                raise InvalidArgument("OOB detected due to %s=%d" %
                                      (self.max_name, args[self.max_name]))

        # Allow the specific case of max=min-1, which disables the loop
        if args[self.max_name] < args[self.min_name] - 1:
            raise InvalidArgument("Illegal %s=%d < %s=%d" %
                                  (self.max_name, args[self.max_name],
                                   self.min_name, args[self.min_name]))
        elif args[self.max_name] == args[self.min_name] - 1:
            debug(
                "%s=%d and %s=%d might cause no iterations along Dimension %s",
                self.min_name, args[self.min_name], self.max_name,
                args[self.max_name], self.name)

    # Pickling support
    _pickle_args = ['name']
    _pickle_kwargs = ['spacing']
    __reduce_ex__ = Pickable.__reduce_ex__
Ejemplo n.º 15
0
 def _C_typedata(self):
     return dtype_to_cstr(self.dtype)
Ejemplo n.º 16
0
 def _C_typename(self):
     return '%s%s' % ('const ' if self._is_const else '',
                      dtype_to_cstr(self.dtype))
Ejemplo n.º 17
0
 def _C_typedata(self):
     return 'ACC<%s>' % dtype_to_cstr(self.dtype)
Ejemplo n.º 18
0
 def _C_typename(self):
     return '%sACC<%s> &' % (
         'const ' if self.read_only else '',
         dtype_to_cstr(self.dtype)
     )
Ejemplo n.º 19
0
 def visit_LocalExpression(self, o):
     return c.Initializer(c.Value(dtype_to_cstr(o.dtype),
                          ccode(o.expr.lhs, dtype=o.dtype)),
                          ccode(o.expr.rhs, dtype=o.dtype))
Ejemplo n.º 20
0
 def _C_typedata(self):
     return 'volatile %s' % dtype_to_cstr(self.dtype)
Ejemplo n.º 21
0
 def _C_typename(self):
     return '%s%s' % ('const ' if self.is_const else '',
                      dtype_to_cstr(self.dtype))
Ejemplo n.º 22
0
class Dimension(AbstractSymbol, ArgProvider):
    """
    Symbol defining an iteration space.

    A Dimension represents a problem dimension. It is typically used to index
    into Functions, but it can also appear in the middle of a symbolic expression
    just like any other symbol.

    Parameters
    ----------
    name : str
        Name of the dimension.
    spacing : symbol, optional
        A symbol to represent the physical spacing along this Dimension.

    Examples
    --------
    Dimensions are automatically created when a Grid is instantiated.

    >>> from devito import Grid
    >>> grid = Grid(shape=(4, 4))
    >>> x, y = grid.dimensions
    >>> type(x)
    <class 'devito.types.dimension.SpaceDimension'>
    >>> time = grid.time_dim
    >>> type(time)
    <class 'devito.types.dimension.TimeDimension'>
    >>> t = grid.stepping_dim
    >>> type(t)
    <class 'devito.types.dimension.SteppingDimension'>

    Alternatively, one can create Dimensions explicitly

    >>> from devito import Dimension
    >>> i = Dimension(name='i')

    Or, when many "free" Dimensions are needed, with the shortcut

    >>> from devito import dimensions
    >>> i, j, k = dimensions('i j k')

    A Dimension can be used to build a Function as well as within symbolic
    expressions, as both array index ("indexed notation") and free symbol.

    >>> from devito import Function
    >>> f = Function(name='f', shape=(4, 4), dimensions=(i, j))
    >>> f + f
    2*f(i, j)
    >>> f[i + 1, j] + f[i, j + 1]
    f[i, j + 1] + f[i + 1, j]
    >>> f*i
    i*f(i, j)
    """

    is_Dimension = True
    is_Space = False
    is_Time = False

    is_Default = False
    is_Derived = False
    is_NonlinearDerived = False
    is_Sub = False
    is_Conditional = False
    is_Stepping = False
    is_Modulo = False
    is_Incr = False

    # Unlike other Symbols, Dimensions can only be integers
    dtype = np.int32
    _C_typename = 'const %s' % dtype_to_cstr(dtype)
    _C_typedata = _C_typename

    def __new__(cls, name, spacing=None):
        return Dimension.__xnew_cached_(cls, name, spacing)

    def __new_stage2__(cls, name, spacing=None):
        newobj = sympy.Symbol.__xnew__(cls, name)
        newobj._spacing = spacing or Scalar(name='h_%s' % name, is_const=True)
        return newobj

    __xnew__ = staticmethod(__new_stage2__)
    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    def __str__(self):
        return self.name

    @property
    def spacing(self):
        """Symbol representing the physical spacing along the Dimension."""
        return self._spacing

    @cached_property
    def symbolic_size(self):
        """Symbolic size of the Dimension."""
        return Scalar(name=self.size_name, dtype=np.int32, is_const=True)

    @cached_property
    def symbolic_min(self):
        """Symbol defining the minimum point of the Dimension."""
        return Scalar(name=self.min_name, dtype=np.int32, is_const=True)

    @cached_property
    def symbolic_max(self):
        """Symbol defining the maximum point of the Dimension."""
        return Scalar(name=self.max_name, dtype=np.int32, is_const=True)

    @cached_property
    def size_name(self):
        return "%s_size" % self.name

    @cached_property
    def min_name(self):
        return "%s_m" % self.name

    @cached_property
    def max_name(self):
        return "%s_M" % self.name

    @property
    def root(self):
        return self

    @property
    def _limits(self):
        return (self.symbolic_min, self.symbolic_max, 1)

    @property
    def _C_name(self):
        return self.name

    @property
    def _properties(self):
        return (self.spacing, )

    def _hashable_content(self):
        return super(Dimension, self)._hashable_content() + self._properties

    @cached_property
    def _defines(self):
        return frozenset({self})

    @property
    def _arg_names(self):
        """Tuple of argument names introduced by the Dimension."""
        return (self.name, self.size_name, self.max_name, self.min_name)

    def _arg_defaults(self, _min=None, size=None, alias=None):
        """
        A map of default argument values defined by the Dimension.

        Parameters
        ----------
        _min : int, optional
            Minimum point as provided by data-carrying objects.
        size : int, optional
            Size as provided by data-carrying symbols.
        alias : Dimension, optional
            To get the min/max/size names under which to store values. Use
            self's if None.
        """
        dim = alias or self
        return {
            dim.min_name: _min or 0,
            dim.size_name: size,
            dim.max_name: size if size is None else size - 1
        }

    def _arg_values(self, args, interval, grid, **kwargs):
        """
        Produce a map of argument values after evaluating user input. If no user
        input is provided, get a known value in ``args`` and adjust it so that no
        out-of-bounds memory accesses will be performeed. The adjustment exploits
        the information in ``interval``, an Interval describing the Dimension data
        space. If no value is available in ``args``, use a default value.

        Parameters
        ----------
        args : dict
            Known argument values.
        interval : Interval
            Description of the Dimension data space.
        grid : Grid
            Only relevant in case of MPI execution; if ``self`` is a distributed
            Dimension, then ``grid`` is used to translate user input into rank-local
            indices.
        **kwargs
            Dictionary of user-provided argument overrides.
        """
        # Fetch user input and convert into rank-local values
        glb_minv = kwargs.pop(self.min_name, None)
        glb_maxv = kwargs.pop(self.max_name, kwargs.pop(self.name, None))
        if grid is not None and grid.is_distributed(self):
            loc_minv, loc_maxv = grid.distributor.glb_to_loc(
                self, (glb_minv, glb_maxv))
        else:
            loc_minv, loc_maxv = glb_minv, glb_maxv

        # If no user-override provided, use a suitable default value
        defaults = self._arg_defaults()
        if glb_minv is None:
            loc_minv = args.get(self.min_name, defaults[self.min_name])
            try:
                loc_minv -= min(interval.lower, 0)
            except (AttributeError, TypeError):
                pass
        if glb_maxv is None:
            loc_maxv = args.get(self.max_name, defaults[self.max_name])
            try:
                loc_maxv -= max(interval.upper, 0)
            except (AttributeError, TypeError):
                pass

        return {self.min_name: loc_minv, self.max_name: loc_maxv}

    def _arg_check(self, args, size, interval):
        """
        Raises
        ------
        InvalidArgument
            If any of the ``self``-related runtime arguments in ``args``
            will cause an out-of-bounds access.
        """
        if self.min_name not in args:
            raise InvalidArgument("No runtime value for %s" % self.min_name)
        if interval.is_Defined and args[self.min_name] + interval.lower < 0:
            raise InvalidArgument("OOB detected due to %s=%d" %
                                  (self.min_name, args[self.min_name]))

        if self.max_name not in args:
            raise InvalidArgument("No runtime value for %s" % self.max_name)
        if interval.is_Defined and args[self.max_name] + interval.upper >= size:
            raise InvalidArgument("OOB detected due to %s=%d" %
                                  (self.max_name, args[self.max_name]))

        # Allow the specific case of max=min-1, which disables the loop
        if args[self.max_name] < args[self.min_name] - 1:
            raise InvalidArgument("Illegal max=%s < min=%s" %
                                  (args[self.max_name], args[self.min_name]))
        elif args[self.max_name] == args[self.min_name] - 1:
            debug(
                "%s=%d and %s=%d might cause no iterations along Dimension %s",
                self.min_name, args[self.min_name], self.max_name,
                args[self.max_name], self.name)

    # Pickling support
    _pickle_args = ['name']
    _pickle_kwargs = ['spacing']
    __reduce_ex__ = Pickable.__reduce_ex__