コード例 #1
0
ファイル: kernelparameters.py プロジェクト: mabau/pystencils
class FieldStrideSymbol(TypedSymbol):
    """Sympy symbol representing the stride value of a field in a specific coordinate."""
    def __new__(cls, *args, **kwds):
        obj = FieldStrideSymbol.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, field_name, coordinate):
        name = f"_stride_{field_name}_{coordinate}"
        obj = super(FieldStrideSymbol, cls).__xnew__(cls,
                                                     name,
                                                     STRIDE_DTYPE,
                                                     positive=True)
        obj.field_name = field_name
        obj.coordinate = coordinate
        return obj

    def __getnewargs__(self):
        return self.field_name, self.coordinate

    def __getnewargs_ex__(self):
        return (self.field_name, self.coordinate), {}

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

    def _hashable_content(self):
        return super()._hashable_content(), self.coordinate, self.field_name
コード例 #2
0
class Scalar(Symbol, ArgProvider):
    """
    Like a Symbol, but in addition it can pass runtime values to an Operator.

    Parameters
    ----------
    name : str
        Name of the symbol.
    dtype : data-type, optional
        Any object that can be interpreted as a numpy data type. Defaults
        to ``np.float32``.
    is_const : bool, optional
        True if the symbol value cannot be modified within an Operator,
        False otherwise. Defaults to False.
    """

    is_Scalar = True

    def __new__(cls, name, dtype=np.float32, is_const=False):
        return Scalar.__xnew_cached_(cls, name, dtype, is_const)

    def __new_stage2__(cls, name, dtype, is_const):
        newobj = Symbol.__xnew__(cls, name, dtype)
        newobj._is_const = is_const
        return newobj

    @property
    def is_const(self):
        return self._is_const

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

    # Pickling support
    _pickle_kwargs = Symbol._pickle_kwargs + ['is_const']
コード例 #3
0
ファイル: kernelparameters.py プロジェクト: mabau/pystencils
class FieldShapeSymbol(TypedSymbol):
    """Sympy symbol representing the shape value of a sequence of fields. In a kernel iterating over multiple fields
    there is only one set of `FieldShapeSymbol`s since all the fields have to be of equal size."""
    def __new__(cls, *args, **kwds):
        obj = FieldShapeSymbol.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, field_names, coordinate):
        names = "_".join([field_name for field_name in field_names])
        name = f"_size_{names}_{coordinate}"
        obj = super(FieldShapeSymbol, cls).__xnew__(cls,
                                                    name,
                                                    SHAPE_DTYPE,
                                                    positive=True)
        obj.field_names = tuple(field_names)
        obj.coordinate = coordinate
        return obj

    def __getnewargs__(self):
        return self.field_names, self.coordinate

    def __getnewargs_ex__(self):
        return (self.field_names, self.coordinate), {}

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

    def _hashable_content(self):
        return super()._hashable_content(), self.coordinate, self.field_names
コード例 #4
0
ファイル: dimension.py プロジェクト: richah2/devito
class ModuloDimension(DerivedDimension):
    """
    Dimension symbol representing a non-contiguous sub-region of a given
    ``parent`` Dimension, which cyclically produces a finite range of values,
    such as ``0, 1, 2, 0, 1, 2, 0, ...``.

    :param parent: Parent dimension from which the ModuloDimension is created.
    :param offset: An integer representing an offset from the parent dimension.
    :param modulo: The extent of the range.
    :param name: (Optional) force a name for this Dimension.
    """
    def __new__(cls, parent, offset, modulo, name=None):
        return ModuloDimension.__xnew_cached_(cls, parent, offset, modulo,
                                              name)

    def __new_stage2__(cls, parent, offset, modulo, name):
        if name is None:
            name = cls._genname(parent.name, (offset, modulo))
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._offset = offset
        newobj._modulo = modulo
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @property
    def offset(self):
        return self._offset

    @property
    def modulo(self):
        return self._modulo

    @property
    def origin(self):
        return self.parent + self.offset

    @cached_property
    def symbolic_start(self):
        return (self.root + self.offset) % self.modulo

    symbolic_incr = symbolic_start

    @property
    def _properties(self):
        return (self._offset, self._modulo)

    def _arg_defaults(self, **kwargs):
        """
        A :class:`ModuloDimension` provides no arguments, so this method
        returns an empty dict.
        """
        return {}

    def _arg_values(self, *args, **kwargs):
        """
        A :class:`ModuloDimension` provides no arguments, so there are
        no argument values to be derived.
        """
        return {}
コード例 #5
0
ファイル: kernelparameters.py プロジェクト: mabau/pystencils
class FieldPointerSymbol(TypedSymbol):
    """Sympy symbol representing the pointer to the beginning of the field data."""
    def __new__(cls, *args, **kwds):
        obj = FieldPointerSymbol.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, field_name, field_dtype, const):
        name = f"_data_{field_name}"
        dtype = PointerType(get_base_type(field_dtype),
                            const=const,
                            restrict=True)
        obj = super(FieldPointerSymbol, cls).__xnew__(cls, name, dtype)
        obj.field_name = field_name
        return obj

    def __getnewargs__(self):
        return self.field_name, self.dtype, self.dtype.const

    def __getnewargs_ex__(self):
        return (self.field_name, self.dtype, self.dtype.const), {}

    def _hashable_content(self):
        return super()._hashable_content(), self.field_name

    __xnew__ = staticmethod(__new_stage2__)
    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))
コード例 #6
0
class TypedSymbol(sp.Symbol):
    def __new__(cls, *args, **kwds):
        obj = TypedSymbol.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, name, dtype, *args, **kwargs):
        obj = super(TypedSymbol, cls).__xnew__(cls, name, *args, **kwargs)
        try:
            obj._dtype = create_type(dtype)
        except (TypeError, ValueError):
            # on error keep the string
            obj._dtype = dtype
        return obj

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

    @property
    def dtype(self):
        return self._dtype

    def _hashable_content(self):
        return super()._hashable_content(), hash(self._dtype)

    def __getnewargs__(self):
        return self.name, self.dtype

    # For reference: Numpy type hierarchy https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html
    @property
    def is_integer(self):
        if hasattr(self.dtype, 'numpy_dtype'):
            return np.issubdtype(self.dtype.numpy_dtype,
                                 np.integer) or super().is_integer
        else:
            return super().is_integer

    @property
    def is_negative(self):
        if hasattr(self.dtype, 'numpy_dtype'):
            if np.issubdtype(self.dtype.numpy_dtype, np.unsignedinteger):
                return False

        return super().is_negative

    @property
    def is_nonnegative(self):
        if self.is_negative is False:
            return True
        else:
            return super().is_nonnegative

    @property
    def is_real(self):
        if hasattr(self.dtype, 'numpy_dtype'):
            return np.issubdtype(self.dtype.numpy_dtype, np.integer) or \
                np.issubdtype(self.dtype.numpy_dtype, np.floating) or \
                super().is_real
        else:
            return super().is_real
コード例 #7
0
class IncrDimension(DerivedDimension):
    """
    Dimension symbol representing a non-contiguous sub-region of a given
    ``parent`` Dimension, with one point every ``step`` points. Thus, if
    ``step == k``, the dimension represents the sequence ``start, start + k,
    start + 2*k, ...``.

    :param parent: Parent dimension from which the IncrDimension is created.
    :param start: An integer representing the starting point of the sequence.
    :param step: The distance between two consecutive points.
    :param name: (Optional) force a name for this Dimension.
    """
    def __new__(cls, parent, start, step, name=None):
        return IncrDimension.__xnew_cached_(cls, parent, start, step, name)

    def __new_stage2__(cls, parent, start, step, name):
        if name is None:
            name = cls._genname(parent.name, (start, step))
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._start = start
        newobj._step = step
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @property
    def step(self):
        return self._step

    @cached_property
    def symbolic_start(self):
        return self._start

    @property
    def symbolic_incr(self):
        return self + self.step

    @property
    def _properties(self):
        return (self._start, self._step)

    def _arg_defaults(self, **kwargs):
        """
        A :class:`IncrDimension` provides no arguments, so this method
        returns an empty dict.
        """
        return {}

    def _arg_values(self, *args, **kwargs):
        """
        A :class:`IncrDimension` provides no arguments, so there are
        no argument values to be derived.
        """
        return {}

    # Pickling support
    _pickle_args = ['parent', 'symbolic_start', 'step']
    _pickle_kwargs = ['name']
コード例 #8
0
class ImmutableSparseMatrix(SparseMatrix, Basic):
    """Create an immutable version of a sparse matrix.

    Examples
    ========

    >>> from sympy import eye
    >>> from sympy.matrices.immutable import ImmutableSparseMatrix
    >>> ImmutableSparseMatrix(1, 1, {})
    Matrix([[0]])
    >>> ImmutableSparseMatrix(eye(3))
    Matrix([
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]])
    >>> _[0, 0] = 42
    Traceback (most recent call last):
    ...
    TypeError: Cannot set values of ImmutableSparseMatrix
    >>> _.shape
    (3, 3)
    """

    is_Matrix = True
    _class_priority = 9

    @classmethod
    def _new(cls, *args, **kwargs):
        s = MutableSparseMatrix(*args)
        rows = Integer(s.rows)
        cols = Integer(s.cols)
        mat = Dict(s._smat)
        obj = Basic.__new__(cls, rows, cols, mat)
        obj.rows = s.rows
        obj.cols = s.cols
        obj._smat = s._smat
        return obj

    def __new__(cls, *args, **kwargs):
        return cls._new(*args, **kwargs)

    def __setitem__(self, *args):
        raise TypeError("Cannot set values of ImmutableSparseMatrix")

    def __hash__(self):
        return hash((type(self).__name__, ) + (self.shape, tuple(self._smat)))

    _eval_Eq = ImmutableDenseMatrix._eval_Eq

    def as_immutable(self):
        return self

    def is_diagonalizable(self, reals_only=False, **kwargs):
        return super(ImmutableSparseMatrix,
                     self).is_diagonalizable(reals_only=reals_only, **kwargs)

    is_diagonalizable.__doc__ = SparseMatrix.is_diagonalizable.__doc__
    is_diagonalizable = cacheit(is_diagonalizable)
コード例 #9
0
class ConditionalDimension(DerivedDimension):

    is_NonlinearDerived = True
    is_Conditional = True
    """
    Dimension symbol representing a sub-region of a given ``parent`` Dimension.
    Unlike a :class:`SubDimension`, a ConditionalDimension does not represent
    a contiguous region. The iterations touched by a ConditionalDimension
    are expressible in two different ways: ::

        * ``factor``: an integer indicating the size of the increment.
        * ``condition``: an arbitrary SymPy expression depending on ``parent``.
                         All iterations for which the expression evaluates to
                         True are part of the ``SubDimension`` region.

    ConditionalDimension needs runtime arguments. The generated C code will require
    the size of the dimension to initialize the arrays as e.g:

        .. code-block:: python
           x = grid.dimension[0]
           x1 = ConditionalDimension(name='x1', parent=x, factor=2)
           u1 = TimeFunction(name='u1', dimensions=(x1,), size=grid.shape[0]/factor)
           # The generated code will look like
           float (*restrict u1)[x1_size + 1] =

    """
    def __new__(cls, name, parent, factor=None, condition=None):
        return ConditionalDimension.__xnew_cached_(cls, name, parent, factor,
                                                   condition)

    def __new_stage2__(cls, name, parent, factor, condition):
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._factor = factor
        newobj._condition = condition
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @property
    def spacing(self):
        return self.factor * self.parent.spacing

    @property
    def factor(self):
        return self._factor

    @property
    def condition(self):
        return self._condition

    @property
    def _properties(self):
        return (self._factor, self._condition)

    # Pickling support
    _pickle_kwargs = DerivedDimension._pickle_kwargs + ['factor', 'condition']
コード例 #10
0
ファイル: data_types.py プロジェクト: mabau/pystencils
class TypedSymbol(sp.Symbol):
    def __new__(cls, *args, **kwds):
        obj = TypedSymbol.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, name, dtype, **kwargs):
        assumptions = assumptions_from_dtype(dtype)
        assumptions.update(kwargs)
        obj = super(TypedSymbol, cls).__xnew__(cls, name, **assumptions)
        try:
            obj._dtype = create_type(dtype)
        except (TypeError, ValueError):
            # on error keep the string
            obj._dtype = dtype
        return obj

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

    @property
    def dtype(self):
        return self._dtype

    def _hashable_content(self):
        return super()._hashable_content(), hash(self._dtype)

    def __getnewargs__(self):
        return self.name, self.dtype

    def __getnewargs_ex__(self):
        return (self.name, self.dtype), self.assumptions0

    @property
    def canonical(self):
        return self

    @property
    def reversed(self):
        return self

    @property
    def headers(self):
        headers = []
        try:
            if np.issubdtype(self.dtype.numpy_dtype, np.complexfloating):
                headers.append('"cuda_complex.hpp"')
        except Exception:
            pass
        try:
            if np.issubdtype(self.dtype.base_type.numpy_dtype, np.complexfloating):
                headers.append('"cuda_complex.hpp"')
        except Exception:
            pass

        return headers
コード例 #11
0
ファイル: operation.py プロジェクト: nc26676027/ArxPy
def _cacheit(func):
    """Cache functions if `CacheContext` is enabled."""
    cfunc = cache.cacheit(func)

    def cached_func(*args, **kwargs):
        if context.Cache.current_context:
            return cfunc(*args, **kwargs)
        else:
            return func(*args, **kwargs)

    return cached_func
コード例 #12
0
ファイル: indexing.py プロジェクト: mabau/pystencils
class ThreadIndexingSymbol(TypedSymbol):
    def __new__(cls, *args, **kwds):
        obj = ThreadIndexingSymbol.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, name, dtype, *args, **kwargs):
        obj = super(ThreadIndexingSymbol,
                    cls).__xnew__(cls, name, dtype, *args, **kwargs)
        return obj

    __xnew__ = staticmethod(__new_stage2__)
    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))
コード例 #13
0
class Signal(Symbol):
    """
    Signal field, in the sense of non-linear response theory.

    This is a specialization of the basic sympy Symbol class.

    Parameters
    ----------
    k : list of ints
        List of wavevector interactions. A third order response would have a
        list of 3 number. The number is the pulse index and the sign designates
        the direction, ie: <0 implies complex conjugate.
        Thus:
            [-1, 2, 3] is the signal due to -k_1+k_2+k_3, the photon echo
            [-1, 1, 3] is the signal due to -k_1+k_1+k_3, a pump-probe signal
            [1] is the linear signal due to the first pulse, on the ket side.

    Note
    ----
    This is designed to be used by the `signal` function.
    """

    # with help from:
    # https://groups.google.com/forum/#!topic/sympy/pU81Trc_Xr8
    def __new_stage2__(cls, name, k, *a, **kw):
        obj = super(Signal, cls).__xnew__(cls, name, *a, **kw)
        obj.k = k
        return obj

    def __new__(cls, k, *a, **kw):
        s = Signal._mk_symbol(k)
        obj = Signal.__xnew_cached_(cls, s, k, *a, **kw)
        return obj

    __xnew__ = staticmethod(__new_stage2__)
    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))
    # _hashable_content should be fine.
    # (The values of k are included in the symbol.)

    @property
    def order(self):
        return len(self.k)

    @staticmethod
    def _mk_symbol(k):
        """
        Make the string to be used as a symbol, eg: '\chi_{k_1+k_2-k_3}'
        """
        lbl = ['+' if i > 0 else '-' for i in k]
        lbl = [s + 'k_' + str(abs(n)) for s, n in zip(lbl, k)]
        return r'\chi_{' + ''.join(lbl) + '}'
コード例 #14
0
class CeMoment(sp.Symbol):
    def __new__(cls, name, *args, **kwargs):
        obj = CeMoment.__xnew_cached_(cls, name, *args, **kwargs)
        return obj

    def __new_stage2__(self, name, moment_tuple, superscript=-1):
        obj = super(CeMoment, self).__xnew__(self, name)
        obj._moment_tuple = moment_tuple
        while len(obj._moment_tuple) < 3:
            obj._moment_tuple = obj._moment_tuple + (0, )
        obj.superscript = superscript
        return obj

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

    def _hashable_content(self):
        super_class_contents = list(super(CeMoment, self)._hashable_content())
        return tuple(
            super_class_contents +
            [hash(repr(self.moment_tuple)),
             hash(repr(self.superscript))])

    @property
    def indices(self):
        return get_moment_indices(self.moment_tuple)

    @property
    def moment_tuple(self):
        return self._moment_tuple

    def __getnewargs__(self):
        return self.name, self.moment_tuple, self.superscript

    def _latex(self, *_):
        coord_str = []
        for i, comp in enumerate(self.moment_tuple):
            coord_str += [str(i)] * comp
        coord_str = "".join(coord_str)
        result = "{%s_{%s}" % (self.name, coord_str)
        if self.superscript >= 0:
            result += "^{(%d)}}" % (self.superscript, )
        else:
            result += "}"
        return result

    def __repr__(self):
        return "%s_(%d)_%s" % (self.name, self.superscript, self.moment_tuple)

    def __str__(self):
        return "%s_(%d)_%s" % (self.name, self.superscript, self.moment_tuple)
コード例 #15
0
class CFunction(TypedSymbol):
    def __new__(cls, function, dtype):
        return CFunction.__xnew_cached_(cls, function, dtype)

    def __new_stage2__(cls, function, dtype):
        return super(CFunction, cls).__xnew__(cls, function, dtype)

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

    def __getnewargs__(self):
        return self.name, self.dtype

    def __getnewargs_ex__(self):
        return (self.name, self.dtype), {}
コード例 #16
0
ファイル: immutable.py プロジェクト: moorepants/sympy
class ImmutableRepMatrix(RepMatrix, MatrixExpr):  # type: ignore
    """Immutable matrix based on RepMatrix

    Uses DomainMAtrix as the internal representation.
    """

    #
    # This is a subclass of RepMatrix that adds/overrides some methods to make
    # the instances Basic and immutable. ImmutableRepMatrix is a superclass for
    # both ImmutableDenseMatrix and ImmutableSparseMatrix.
    #

    def __new__(cls, *args, **kwargs):
        return cls._new(*args, **kwargs)

    __hash__ = MatrixExpr.__hash__

    def copy(self):
        return self

    @property
    def cols(self):
        return self._cols

    @property
    def rows(self):
        return self._rows

    @property
    def shape(self):
        return self._rows, self._cols

    def as_immutable(self):
        return self

    def _entry(self, i, j, **kwargs):
        return self[i, j]

    def __setitem__(self, *args):
        raise TypeError("Cannot set values of {}".format(self.__class__))

    def is_diagonalizable(self, reals_only=False, **kwargs):
        return super().is_diagonalizable(reals_only=reals_only, **kwargs)

    is_diagonalizable.__doc__ = SparseRepMatrix.is_diagonalizable.__doc__
    is_diagonalizable = cacheit(is_diagonalizable)
コード例 #17
0
ファイル: data_types.py プロジェクト: mabau/pystencils
class TypedImaginaryUnit(TypedSymbol):
    def __new__(cls, *args, **kwds):
        obj = TypedImaginaryUnit.__xnew_cached_(cls, *args, **kwds)
        return obj

    def __new_stage2__(cls, dtype):
        obj = super(TypedImaginaryUnit, cls).__xnew__(cls,
                                                      "_i",
                                                      dtype,
                                                      imaginary=True)
        return obj

    headers = ['"cuda_complex.hpp"']

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

    def __getnewargs__(self):
        return (self.dtype,)

    def __getnewargs_ex__(self):
        return (self.dtype,), {}
コード例 #18
0
class SubDimension(DerivedDimension):
    """
    Symbol defining a convex iteration sub-space derived from a ``parent``
    Dimension.

    Parameters
    ----------
    name : str
        Name of the dimension.
    parent : Dimension
        The parent Dimension.
    left : expr-like
        Symbolic expression providing the left (lower) bound of the
        SubDimension.
    right : expr-like
        Symbolic expression providing the right (upper) bound of the
        SubDimension.
    thickness : 2-tuple of 2-tuples
        The thickness of the left and right regions, respectively.
    local : bool
        True if, in case of domain decomposition, the SubDimension is
        guaranteed not to span more than one domains, False otherwise.

    Examples
    --------
    SubDimensions should *not* be created directly in user code; SubDomains
    should be used instead. Exceptions are rare.

    To create a SubDimension, one should use the shortcut methods ``left``,
    ``right``, ``middle``. For example, to create a SubDimension that spans
    the entire space of the parent Dimension except for the two extremes:

    >>> from devito import Dimension, SubDimension
    >>> x = Dimension('x')
    >>> xi = SubDimension.middle('xi', x, 1, 1)

    For a SubDimension that only spans the three leftmost points of its
    parent Dimension, instead:

    >>> xl = SubDimension.left('xl', x, 3)

    SubDimensions created via the ``left`` and ``right`` shortcuts are, by default,
    local (i.e., non-distributed) Dimensions, as they are assumed to fit entirely
    within a single domain. This is the most typical use case (e.g., to set up
    boundary conditions). To drop this assumption, pass ``local=False``.
    """

    is_Sub = True

    def __new__(cls, name, parent, left, right, thickness, local):
        return SubDimension.__xnew_cached_(cls, name, parent, left, right,
                                           thickness, local)

    def __new_stage2__(cls, name, parent, left, right, thickness, local):
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._interval = sympy.Interval(left, right)
        newobj._thickness = cls._Thickness(*thickness)
        newobj._local = local
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    _Thickness = namedtuple('Thickness', 'left right')

    @classmethod
    def left(cls, name, parent, thickness, local=True):
        lst, rst = cls._symbolic_thickness(name)
        return cls(name,
                   parent,
                   left=parent.symbolic_min,
                   right=parent.symbolic_min + lst - 1,
                   thickness=((lst, thickness), (rst, 0)),
                   local=local)

    @classmethod
    def right(cls, name, parent, thickness, local=True):
        lst, rst = cls._symbolic_thickness(name)
        return cls(name,
                   parent,
                   left=parent.symbolic_max - rst + 1,
                   right=parent.symbolic_max,
                   thickness=((lst, 0), (rst, thickness)),
                   local=local)

    @classmethod
    def middle(cls,
               name,
               parent,
               thickness_left,
               thickness_right,
               local=False):
        lst, rst = cls._symbolic_thickness(name)
        return cls(name,
                   parent,
                   left=parent.symbolic_min + lst,
                   right=parent.symbolic_max - rst,
                   thickness=((lst, thickness_left), (rst, thickness_right)),
                   local=local)

    @property
    def symbolic_min(self):
        return self._interval.left

    @property
    def symbolic_max(self):
        return self._interval.right

    @property
    def local(self):
        return self._local

    @property
    def thickness(self):
        return self._thickness

    @property
    def _maybe_distributed(self):
        return not self.local

    @classmethod
    def _symbolic_thickness(cls, name):
        return (Scalar(name="%s_ltkn" % name, dtype=np.int32, is_const=True),
                Scalar(name="%s_rtkn" % name, dtype=np.int32, is_const=True))

    @cached_property
    def _thickness_map(self):
        return dict(self.thickness)

    def _offset_left(self):
        # The left extreme of the SubDimension can be related to either the
        # min or max of the parent dimension
        try:
            symbolic_thickness = self.symbolic_min - self.parent.symbolic_min
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_min
        except TypeError:
            symbolic_thickness = self.symbolic_min - self.parent.symbolic_max
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_max

    def _offset_right(self):
        # The right extreme of the SubDimension can be related to either the
        # min or max of the parent dimension
        try:
            symbolic_thickness = self.symbolic_max - self.parent.symbolic_min
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_min
        except TypeError:
            symbolic_thickness = self.symbolic_max - self.parent.symbolic_max
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_max

    @property
    def _properties(self):
        return (self._interval, self.thickness, self.local)

    def _arg_defaults(self, grid=None, **kwargs):
        if grid is not None and grid.is_distributed(self.root):
            # Get local thickness
            ltkn = grid.distributor.glb_to_loc(self.root,
                                               self.thickness.left[1], LEFT)
            rtkn = grid.distributor.glb_to_loc(self.root,
                                               self.thickness.right[1], RIGHT)
            return {
                i.name: v
                for i, v in zip(self._thickness_map, (ltkn, rtkn))
            }
        else:
            return {k.name: v for k, v in self.thickness}

    def _arg_values(self, args, interval, grid, **kwargs):
        return self._arg_defaults(grid=grid, **kwargs)

    # Pickling support
    _pickle_args = DerivedDimension._pickle_args +\
        ['symbolic_min', 'symbolic_max', 'thickness', 'local']
    _pickle_kwargs = []
コード例 #19
0
class ImmutableSparseMatrix(SparseMatrix, MatrixExpr):
    """Create an immutable version of a sparse matrix.

    Examples
    ========

    >>> from sympy import eye
    >>> from sympy.matrices.immutable import ImmutableSparseMatrix
    >>> ImmutableSparseMatrix(1, 1, {})
    Matrix([[0]])
    >>> ImmutableSparseMatrix(eye(3))
    Matrix([
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]])
    >>> _[0, 0] = 42
    Traceback (most recent call last):
    ...
    TypeError: Cannot set values of ImmutableSparseMatrix
    >>> _.shape
    (3, 3)
    """
    is_Matrix = True
    _class_priority = 9

    def __new__(cls, *args, **kwargs):
        return cls._new(*args, **kwargs)

    __hash__ = MatrixExpr.__hash__

    @classmethod
    def _new(cls, *args, **kwargs):
        rows, cols, smat = cls._handle_creation_inputs(*args, **kwargs)
        obj = Basic.__new__(cls, Integer(rows), Integer(cols), Dict(smat))
        obj._rows = rows
        obj._cols = cols
        obj._smat = smat
        return obj

    def __setitem__(self, *args):
        raise TypeError("Cannot set values of ImmutableSparseMatrix")

    def _entry(self, i, j, **kwargs):
        return SparseMatrix.__getitem__(self, (i, j))

    _eval_Eq = ImmutableDenseMatrix._eval_Eq

    @property
    def cols(self):
        return self._cols

    @property
    def rows(self):
        return self._rows

    @property
    def shape(self):
        return self._rows, self._cols

    def as_immutable(self):
        return self

    def is_diagonalizable(self, reals_only=False, **kwargs):
        return super().is_diagonalizable(
            reals_only=reals_only, **kwargs)
    is_diagonalizable.__doc__ = SparseMatrix.is_diagonalizable.__doc__
    is_diagonalizable = cacheit(is_diagonalizable)
コード例 #20
0
ファイル: dimension.py プロジェクト: rcluan/devito
class ModuloDimension(DerivedDimension):
    """
    Dimension symbol representing a non-contiguous sub-region of a given
    ``parent`` Dimension, which cyclically produces a finite range of values,
    such as ``0, 1, 2, 0, 1, 2, 0, ...``.

    Parameters
    ----------
    parent : Dimension
        The Dimension from which the ModuloDimension is derived.
    offset : int
        The offset from the parent dimension
    modulo : int
        The divisor value.
    name : str, optional
        To force a different Dimension name.

    Notes
    -----
    This type should not be instantiated directly in user code; if in need for
    modulo buffered iteration, use SteppingDimension instead.
    """

    is_Modulo = True

    def __new__(cls, parent, offset, modulo, name=None):
        return ModuloDimension.__xnew_cached_(cls, parent, offset, modulo,
                                              name)

    def __new_stage2__(cls, parent, offset, modulo, name):
        if name is None:
            name = cls._genname(parent.name, (offset, modulo))
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._offset = offset
        newobj._modulo = modulo
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @property
    def offset(self):
        return self._offset

    @property
    def modulo(self):
        return self._modulo

    @property
    def origin(self):
        return self.parent + self.offset

    @cached_property
    def symbolic_min(self):
        return (self.root + self.offset) % self.modulo

    symbolic_incr = symbolic_min

    @property
    def _properties(self):
        return (self._offset, self._modulo)

    def _arg_defaults(self, **kwargs):
        """
        A ModuloDimension provides no arguments, so this method returns an empty dict.
        """
        return {}

    def _arg_values(self, *args, **kwargs):
        """
        A ModuloDimension provides no arguments, so there are no argument values
        to be derived.
        """
        return {}

    # Pickling support
    _pickle_args = ['parent', 'offset', 'modulo']
    _pickle_kwargs = ['name']
コード例 #21
0
ファイル: dimension.py プロジェクト: rcluan/devito
class IncrDimension(DerivedDimension):
    """
    Dimension symbol representing a non-contiguous sub-region of a given
    ``parent`` Dimension, with one point every ``step`` points. Thus, if
    ``step == k``, the dimension represents the sequence ``min, min + k,
    min + 2*k, ...``.

    Parameters
    ----------
    parent : Dimension
        The Dimension from which the IncrDimension is derived.
    _min : int, optional
        The minimum point of the sequence. Defaults to the parent's
        symbolic minimum.
    step : int, optional
        The distance between two consecutive points. Defaults to the
        symbolic size.
    name : str, optional
        To force a different Dimension name.

    Notes
    -----
    This type should not be instantiated directly in user code.
    """

    is_Incr = True

    def __new__(cls, parent, _min=None, step=None, name=None):
        return IncrDimension.__xnew_cached_(cls, parent, _min, step, name)

    def __new_stage2__(cls, parent, _min, step, name):
        if name is None:
            name = cls._genname(parent.name, (_min, step))
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._min = _min
        newobj._step = step
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @cached_property
    def step(self):
        return self._step if self._step is not None else self.symbolic_size

    @cached_property
    def max_step(self):
        return self.parent.symbolic_max - self.parent.symbolic_min + 1

    @cached_property
    def symbolic_min(self):
        return self._min if self._min is not None else self.parent.symbolic_min

    @property
    def symbolic_incr(self):
        return self + self.step

    @property
    def _properties(self):
        return (self._min, self._step)

    def _arg_defaults(self, **kwargs):
        """
        An IncrDimension provides no arguments, so this method returns an empty dict.
        """
        return {}

    def _arg_values(self, *args, **kwargs):
        """
        An IncrDimension provides no arguments, so there are no argument values to
        be derived.
        """
        return {}

    # Pickling support
    _pickle_args = ['parent', 'symbolic_min', 'step']
    _pickle_kwargs = ['name']
コード例 #22
0
ファイル: dimension.py プロジェクト: rcluan/devito
class SubDimension(DerivedDimension):
    """
    Symbol defining a convex iteration sub-space derived from a ``parent``
    Dimension.

    Parameters
    ----------
    name : str
        Name of the dimension.
    parent : Dimension
        The parent Dimension.
    left : expr-like
        Symbolic expression providing the left (lower) bound of the
        SubDimension.
    right : expr-like
        Symbolic expression providing the right (upper) bound of the
        SubDimension.
    thickness : 2-tuple of 2-tuples
        The thickness of the left and right regions, respectively.

    Examples
    --------
    Apart from rare circumstances, SubDimensions should *not* be created
    directly in user code; SubDomains should be used instead.

    To create a SubDimension, one typically uses the shortcut methods ``left``,
    ``right``, ``middle``. For example, to create a SubDimension that spans
    the entire space of the parent Dimension except for the two extremes, one
    could proceed as follows

    >>> from devito import Dimension, SubDimension
    >>> x = Dimension('x')
    >>> xi = SubDimension.middle('xi', x, 1, 1)

    For a SubDimension that only spans the three leftmost points of its
    parent Dimension, instead

    >>> xl = SubDimension.left('xl', x, 3)
    """

    is_Sub = True

    def __new__(cls, name, parent, left, right, thickness):
        return SubDimension.__xnew_cached_(cls, name, parent, left, right,
                                           thickness)

    def __new_stage2__(cls, name, parent, left, right, thickness):
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._interval = sympy.Interval(left, right)
        newobj._thickness = cls._Thickness(*thickness)
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    _Thickness = namedtuple('Thickness', 'left right')

    @classmethod
    def left(cls, name, parent, thickness):
        lst, rst = cls._symbolic_thickness(name)
        return cls(name,
                   parent,
                   left=parent.symbolic_min,
                   right=parent.symbolic_min + lst - 1,
                   thickness=((lst, thickness), (rst, 0)))

    @classmethod
    def right(cls, name, parent, thickness):
        lst, rst = cls._symbolic_thickness(name)
        return cls(name,
                   parent,
                   left=parent.symbolic_max - rst + 1,
                   right=parent.symbolic_max,
                   thickness=((lst, 0), (rst, thickness)))

    @classmethod
    def middle(cls, name, parent, thickness_left, thickness_right):
        lst, rst = cls._symbolic_thickness(name)
        return cls(name,
                   parent,
                   left=parent.symbolic_min + lst,
                   right=parent.symbolic_max - rst,
                   thickness=((lst, thickness_left), (rst, thickness_right)))

    @property
    def symbolic_min(self):
        return self._interval.left

    @property
    def symbolic_max(self):
        return self._interval.right

    @property
    def thickness(self):
        return self._thickness

    @classmethod
    def _symbolic_thickness(cls, name):
        return (Scalar(name="%s_ltkn" % name, dtype=np.int32, is_const=True),
                Scalar(name="%s_rtkn" % name, dtype=np.int32, is_const=True))

    @cached_property
    def _thickness_map(self):
        return dict(self.thickness)

    def _offset_left(self):
        # The left extreme of the SubDimension can be related to either the
        # min or max of the parent dimension
        try:
            symbolic_thickness = self.symbolic_min - self.parent.symbolic_min
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_min
        except TypeError:
            symbolic_thickness = self.symbolic_min - self.parent.symbolic_max
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_max

    def _offset_right(self):
        # The right extreme of the SubDimension can be related to either the
        # min or max of the parent dimension
        try:
            symbolic_thickness = self.symbolic_max - self.parent.symbolic_min
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_min
        except TypeError:
            symbolic_thickness = self.symbolic_max - self.parent.symbolic_max
            val = symbolic_thickness.subs(self._thickness_map)
            return int(val), self.parent.symbolic_max

    @property
    def _properties(self):
        return (self._interval, self.thickness)

    def _arg_defaults(self, grid=None, **kwargs):
        if grid is not None and grid.is_distributed(self.root):
            # Get local thickness
            ltkn = grid.distributor.glb_to_loc(self.root,
                                               self.thickness.left[1], LEFT)
            rtkn = grid.distributor.glb_to_loc(self.root,
                                               self.thickness.right[1], RIGHT)
            return {
                i.name: v
                for i, v in zip(self._thickness_map, (ltkn, rtkn))
            }
        else:
            return {k.name: v for k, v in self.thickness}

    def _arg_values(self, args, interval, grid, **kwargs):
        return self._arg_defaults(grid=grid, **kwargs)

    # Pickling support
    _pickle_args = DerivedDimension._pickle_args +\
        ['symbolic_min', 'symbolic_max', 'thickness']
    _pickle_kwargs = []
コード例 #23
0
ファイル: dimension.py プロジェクト: rcluan/devito
class ConditionalDimension(DerivedDimension):
    """
    Symbol defining a non-convex iteration sub-space derived from a ``parent``
    Dimension, implemented by the compiler generating conditional "if-then" code
    within the parent Dimension's iteration space.

    Parameters
    ----------
    name : str
        Name of the dimension.
    parent : Dimension
        The parent Dimension.
    factor : int, optional
        The number of iterations between two executions of the if-branch. If None
        (default), ``condition`` must be provided.
    condition : expr-like, optional
        An arbitrary SymPy expression, typically involving the ``parent``
        Dimension. When it evaluates to True, the if-branch is executed. If None
        (default), ``factor`` must be provided.
    indirect : bool, optional
        If True, use ``condition``, rather than the parent Dimension, to
        index into arrays. A typical use case is when arrays are accessed
        indirectly via the ``condition`` expression. Defaults to False.

    Examples
    --------
    Among the other things, ConditionalDimensions are indicated to implement
    Function subsampling. In the following example, an Operator evaluates the
    Function ``g`` and saves its content into ``f`` every ``factor=4`` iterations.

    >>> from devito import Dimension, ConditionalDimension, Function, Eq, Operator
    >>> size, factor = 16, 4
    >>> i = Dimension(name='i')
    >>> ci = ConditionalDimension(name='ci', parent=i, factor=factor)
    >>> g = Function(name='g', shape=(size,), dimensions=(i,))
    >>> f = Function(name='f', shape=(size/factor,), dimensions=(ci,))
    >>> op = Operator([Eq(g, 1), Eq(f, g)])

    The Operator generates the following for-loop (pseudocode)

    .. code-block:: C

        for (int i = i_m; i <= i_M; i += 1) {
          g[i] = 1;
          if (i%4 == 0) {
            f[i / 4] = g[i];
          }
        }

    Another typical use case is when one needs to constrain the execution of
    loop iterations to make sure certain conditions are honoured. The following
    artificial example employs indirect array accesses and uses ConditionalDimension
    to guard against out-of-bounds accesses.

    >>> from sympy import And
    >>> ci = ConditionalDimension(name='ci', parent=i,
    ...                           condition=And(g[i] > 0, g[i] < 4, evaluate=False))
    >>> f = Function(name='f', shape=(size/factor,), dimensions=(ci,))
    >>> op = Operator(Eq(f[g[i]], f[g[i]] + 1))

    The Operator generates the following for-loop (pseudocode)

    .. code-block:: C

        for (int i = i_m; i <= i_M; i += 1) {
          if (g[i] > 0 && g[i] < 4) {
            f[g[i]] = f[g[i]] + 1;
          }
        }

    """

    is_NonlinearDerived = True
    is_Conditional = True

    def __new__(cls,
                name,
                parent,
                factor=None,
                condition=None,
                indirect=False):
        return ConditionalDimension.__xnew_cached_(cls, name, parent, factor,
                                                   condition, indirect)

    def __new_stage2__(cls, name, parent, factor, condition, indirect):
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._factor = factor
        newobj._condition = condition
        newobj._indirect = indirect
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @property
    def spacing(self):
        return self.factor * self.parent.spacing

    @property
    def factor(self):
        """"""
        return self._factor if self._factor is not None else 1

    @property
    def condition(self):
        return self._condition

    @property
    def indirect(self):
        return self._indirect

    @property
    def index(self):
        return self if self.indirect is True else self.parent

    @property
    def _properties(self):
        return (self._factor, self._condition, self._indirect)

    # Pickling support
    _pickle_kwargs = DerivedDimension._pickle_kwargs + [
        'factor', 'condition', 'indirect'
    ]
コード例 #24
0
ファイル: dimension.py プロジェクト: rcluan/devito
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__
コード例 #25
0
ファイル: dimension.py プロジェクト: rcluan/devito
class DerivedDimension(Dimension):
    """
    Symbol defining an iteration space derived from a ``parent`` Dimension.

    Parameters
    ----------
    name : str
        Name of the dimension.
    parent : Dimension
        The parent Dimension.
    """

    is_Derived = True

    _keymap = {}
    """Map all seen instance `_properties` to a unique number. This is used
    to create unique Dimension names."""
    def __new__(cls, name, parent):
        return DerivedDimension.__xnew_cached_(cls, name, parent)

    def __new_stage2__(cls, name, parent):
        assert isinstance(parent, Dimension)
        newobj = sympy.Symbol.__xnew__(cls, name)
        newobj._parent = parent
        # Inherit time/space identifiers
        newobj.is_Time = parent.is_Time
        newobj.is_Space = parent.is_Space
        return newobj

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

    @classmethod
    def _gensuffix(cls, key):
        return cls._keymap.setdefault(key, len(cls._keymap))

    @classmethod
    def _genname(cls, prefix, key):
        return "%s%d" % (prefix, cls._gensuffix(key))

    @property
    def parent(self):
        return self._parent

    @property
    def root(self):
        return self._parent.root

    @property
    def spacing(self):
        return self.parent.spacing

    @property
    def _properties(self):
        return ()

    def _hashable_content(self):
        return (self.name, self.parent._hashable_content()) + self._properties

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

    @property
    def _arg_names(self):
        return self.parent._arg_names

    def _arg_check(self, *args):
        """A DerivedDimension performs no runtime checks."""
        return

    # Pickling support
    _pickle_args = Dimension._pickle_args + ['parent']
    _pickle_kwargs = []
コード例 #26
0
ファイル: dimension.py プロジェクト: ponykid/SNIST
class Dimension(AbstractSymbol, ArgProvider):

    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

    """
    A Dimension is a symbol representing a problem dimension and thus defining a
    potential iteration space.

    :param name: Name of the dimension symbol.
    :param spacing: Optional, symbol for the spacing along this dimension.
    """

    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)
        return newobj

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

    def __str__(self):
        return self.name

    @property
    def dtype(self):
        # TODO: Do dimensions really need a dtype?
        return np.int32

    @cached_property
    def symbolic_size(self):
        """The symbolic size of this dimension."""
        return Scalar(name=self.size_name, dtype=np.int32)

    @cached_property
    def symbolic_start(self):
        """
        The symbol defining the iteration start for this dimension.
        """
        return Scalar(name=self.min_name, dtype=np.int32)

    @cached_property
    def symbolic_end(self):
        """
        The symbol defining the iteration end for this dimension.
        """
        return Scalar(name=self.max_name, dtype=np.int32)

    @property
    def limits(self):
        return (self.symbolic_start, self.symbolic_end, 1)

    @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 spacing(self):
        return self._spacing

    @property
    def base(self):
        return self

    @property
    def root(self):
        return self

    @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):
        """Return a tuple of argument names introduced by this dimension."""
        return (self.name, self.size_name, self.max_name, self.min_name)

    def _arg_defaults(self, start=None, size=None, alias=None):
        """
        Returns a map of default argument values defined by this dimension.

        :param start: (Optional) known starting point as provided by
                      data-carrying symbols.
        :param size: (Optional) known size as provided by data-carrying symbols.
        :param alias: (Optional) name under which to store values.
        """
        dim = alias or self
        return {dim.min_name: start 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):
        """
        Returns 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``, a :class:`Interval`
        describing the data space of this dimension. If there is no known value
        in ``args``, use a default value.

        :param args: Dictionary of known argument values.
        :param interval: A :class:`Interval` for ``self``.
        :param grid: A :class:`Grid`; if ``self`` is a distributed Dimension in
                     ``grid``, then the user input is translated into rank-local
                     indices.
        :param 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__
コード例 #27
0
class AbstractSymbol(sympy.Symbol, Basic, Pickable):
    """
    Base class for dimension-free symbols.

    The sub-hierarchy is structured as follows

                             AbstractSymbol
                                   |
                 -------------------------------------
                 |                                   |
        AbstractCachedSymbol                --------------------
                 |                          |                  |
              Constant                    Symbol           Dimension
                                            |
                                          Scalar

    There are three relevant AbstractSymbol sub-types: ::

        * Symbol: A generic scalar symbol that can be used to build an equation.
                  It does not carry data. Typically, Symbols are created internally
                  by Devito (e.g., for temporary variables)
        * Constant: A generic scalar symbol that can be used to build an equation.
                    It carries data (a scalar value).
        * Dimension: A problem dimension, used to create an iteration space. It
                     may be used to build equations; typically, it is used as
                     an index for an Indexed.

    Notes
    -----
    Constants are cached by both SymPy and Devito, while Symbols and Dimensions
    by SymPy only.
    """

    is_AbstractSymbol = True

    def __new__(cls, name, dtype=np.int32):
        return AbstractSymbol.__xnew_cached_(cls, name, dtype)

    def __new_stage2__(cls, name, dtype):
        newobj = sympy.Symbol.__xnew__(cls, name)
        newobj._dtype = dtype
        return newobj

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

    @property
    def dtype(self):
        """The data type of the object."""
        return self._dtype

    @property
    def indices(self):
        return ()

    @property
    def shape(self):
        return ()

    @property
    def ndim(self):
        return 0

    @property
    def symbolic_shape(self):
        return ()

    @property
    def base(self):
        return self

    @property
    def function(self):
        return self

    def indexify(self):
        return self

    def _subs(self, old, new, **hints):
        """
        This stub allows sympy.Basic.subs to operate on an expression
        involving devito Scalars.  Ordinarily the comparisons between
        devito subclasses of sympy types are quite strict.
        """
        try:
            if old.name == self.name:
                return new
        except AttributeError:
            pass

        return self

    @property
    def is_const(self):
        """
        True if the symbol value cannot be modified within an Operator (and thus
        its value is provided by the user directly from Python-land), False otherwise.
        """
        return False

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

    @property
    def _C_typename(self):
        return '%s%s' % ('const ' if self.is_const else '',
                         dtype_to_cstr(self.dtype))

    @property
    def _C_typedata(self):
        return dtype_to_cstr(self.dtype)

    @property
    def _C_ctype(self):
        return dtype_to_ctype(self.dtype)

    # Pickling support
    _pickle_args = ['name']
    _pickle_kwargs = ['dtype']
    __reduce_ex__ = Pickable.__reduce_ex__
コード例 #28
0
class ImmutableDenseMatrix(DenseMatrix, MatrixExpr):
    """Create an immutable version of a matrix.

    Examples
    ========

    >>> from sympy import eye
    >>> from sympy.matrices import ImmutableMatrix
    >>> ImmutableMatrix(eye(3))
    Matrix([
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]])
    >>> _[0, 0] = 42
    Traceback (most recent call last):
    ...
    TypeError: Cannot set values of ImmutableDenseMatrix
    """

    # MatrixExpr is set as NotIterable, but we want explicit matrices to be
    # iterable
    _iterable = True
    _class_priority = 8
    _op_priority = 10.001

    def __new__(cls, *args, **kwargs):
        return cls._new(*args, **kwargs)

    __hash__ = MatrixExpr.__hash__

    @classmethod
    def _new(cls, *args, **kwargs):
        if len(args) == 1 and isinstance(args[0], ImmutableDenseMatrix):
            return args[0]
        if kwargs.get('copy', True) is False:
            if len(args) != 3:
                raise TypeError("'copy=False' requires a matrix be initialized as rows,cols,[list]")
            rows, cols, flat_list = args
        else:
            rows, cols, flat_list = cls._handle_creation_inputs(*args, **kwargs)
            flat_list = list(flat_list) # create a shallow copy
        rows = Integer(rows)
        cols = Integer(cols)
        if not isinstance(flat_list, Tuple):
            flat_list = Tuple(*flat_list)

        return Basic.__new__(cls, rows, cols, flat_list)

    @property
    def _mat(self):
        # self.args[2] is a Tuple.  Access to the elements
        # of a tuple are significantly faster than Tuple,
        # so return the internal tuple.
        return self.args[2].args

    def _entry(self, i, j, **kwargs):
        return DenseMatrix.__getitem__(self, (i, j))

    def __setitem__(self, *args):
        raise TypeError("Cannot set values of {}".format(self.__class__))

    def _eval_Eq(self, other):
        """Helper method for Equality with matrices.

        Relational automatically converts matrices to ImmutableDenseMatrix
        instances, so this method only applies here.  Returns True if the
        matrices are definitively the same, False if they are definitively
        different, and None if undetermined (e.g. if they contain Symbols).
        Returning None triggers default handling of Equalities.

        """
        if not hasattr(other, 'shape') or self.shape != other.shape:
            return S.false
        if isinstance(other, MatrixExpr) and not isinstance(
                other, ImmutableDenseMatrix):
            return None
        diff = self - other
        return sympify(diff.is_zero)

    def _eval_extract(self, rowsList, colsList):
        # self._mat is a Tuple.  It is slightly faster to index a
        # tuple over a Tuple, so grab the internal tuple directly
        mat = self._mat
        cols = self.cols
        indices = (i * cols + j for i in rowsList for j in colsList)
        return self._new(len(rowsList), len(colsList),
                         Tuple(*(mat[i] for i in indices), sympify=False), copy=False)

    @property
    def cols(self):
        return int(self.args[1])

    @property
    def rows(self):
        return int(self.args[0])

    @property
    def shape(self):
        return tuple(int(i) for i in self.args[:2])

    def is_diagonalizable(self, reals_only=False, **kwargs):
        return super(ImmutableDenseMatrix, self).is_diagonalizable(
            reals_only=reals_only, **kwargs)
    is_diagonalizable.__doc__ = DenseMatrix.is_diagonalizable.__doc__
    is_diagonalizable = cacheit(is_diagonalizable)
コード例 #29
0
ファイル: dimension.py プロジェクト: ponykid/SNIST
class SubDimension(DerivedDimension):

    is_Sub = True

    """
    Dimension symbol representing a contiguous sub-region of a given
    ``parent`` Dimension.

    :param name: Name of the dimension symbol.
    :param parent: Parent dimension from which the SubDimension is created.
    :param left: Symbolic expression to provide the left bound.
    :param right: Symbolic expression to provide the right bound.
    :param thickness: A 2-tuple of 2-tuples, ``((symbol, int), (symbol, int))``,
                      representing the thickness of the left and right regions,
                      respectively.
    """

    def __new__(cls, name, parent, left, right, thickness):
        return SubDimension.__xnew_cached_(cls, name, parent, left, right, thickness)

    def __new_stage2__(cls, name, parent, left, right, thickness):
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._interval = sympy.Interval(left, right)
        newobj._thickness = cls.Thickness(*thickness)
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    Thickness = namedtuple('Thickness', 'left right')

    @classmethod
    def left(cls, name, parent, thickness):
        lst, rst = cls.symbolic_thickness(name)
        return cls(name, parent,
                   left=parent.symbolic_start,
                   right=parent.symbolic_start+lst-1,
                   thickness=((lst, thickness), (rst, 0)))

    @classmethod
    def right(cls, name, parent, thickness):
        lst, rst = cls.symbolic_thickness(name)
        return cls(name, parent,
                   left=parent.symbolic_end-rst+1,
                   right=parent.symbolic_end,
                   thickness=((lst, 0), (rst, thickness)))

    @classmethod
    def middle(cls, name, parent, thickness_left, thickness_right):
        lst, rst = cls.symbolic_thickness(name)
        return cls(name, parent,
                   left=parent.symbolic_start+lst,
                   right=parent.symbolic_end-rst,
                   thickness=((lst, thickness_left), (rst, thickness_right)))

    @classmethod
    def symbolic_thickness(cls, name):
        return (Scalar(name="%s_ltkn" % name, dtype=np.int32),
                Scalar(name="%s_rtkn" % name, dtype=np.int32))

    @property
    def symbolic_start(self):
        return self._interval.left

    @property
    def symbolic_end(self):
        return self._interval.right

    @cached_property
    def thickness_map(self):
        return dict(self._thickness)

    @property
    def thickness(self):
        return self._thickness

    def offset_left(self):
        # The left extreme of the SubDimension can be related to either the
        # start or end of the parent dimension
        try:
            symbolic_thickness = self.symbolic_start - self.parent.symbolic_start
            val = symbolic_thickness.subs(self.thickness_map)
            return int(val), self.parent.symbolic_start
        except TypeError:
            symbolic_thickness = self.symbolic_start - self.parent.symbolic_end
            val = symbolic_thickness.subs(self.thickness_map)
            return int(val), self.parent.symbolic_end

    def offset_right(self):
        # The right extreme of the SubDimension can be related to either the
        # start or end of the parent dimension
        try:
            symbolic_thickness = self.symbolic_end - self.parent.symbolic_start
            val = symbolic_thickness.subs(self.thickness_map)
            return int(val), self.parent.symbolic_start
        except TypeError:
            symbolic_thickness = self.symbolic_end - self.parent.symbolic_end
            val = symbolic_thickness.subs(self.thickness_map)
            return int(val), self.parent.symbolic_end

    @property
    def _properties(self):
        return (self._interval, self._thickness)

    def _arg_defaults(self, grid=None, **kwargs):
        if grid is not None and grid.is_distributed(self.root):
            # Get local thickness
            ltkn = grid.distributor.glb_to_loc(self.root, self.thickness.left[1], LEFT)
            rtkn = grid.distributor.glb_to_loc(self.root, self.thickness.right[1], RIGHT)
            return {i.name: v for i, v in zip(self.thickness_map, (ltkn, rtkn))}
        else:
            return {k.name: v for k, v in self.thickness}

    def _arg_values(self, args, interval, grid, **kwargs):
        return self._arg_defaults(grid=grid, **kwargs)

    # Pickling support
    _pickle_args = DerivedDimension._pickle_args +\
        ['symbolic_start', 'symbolic_end', 'thickness']
    _pickle_kwargs = []
コード例 #30
0
ファイル: dimension.py プロジェクト: ponykid/SNIST
class ConditionalDimension(DerivedDimension):

    is_NonlinearDerived = True
    is_Conditional = True

    """
    Dimension symbol representing a sub-region of a given ``parent`` Dimension.
    Unlike a :class:`SubDimension`, a ConditionalDimension does not represent
    a contiguous region. The iterations touched by a ConditionalDimension
    are expressible in two different ways: ::

        * ``factor``: an integer indicating the size of the increment.
        * ``condition``: an arbitrary SymPy expression depending on ``parent``.
                         All iterations for which the expression evaluates to
                         True are part of the ``ConditionalDimension`` region.

    ConditionalDimension needs runtime arguments. The generated C code will require
    the size of the dimension to initialize the arrays as e.g:

        .. code-block:: python
           x = grid.dimension[0]
           x1 = ConditionalDimension(name='x1', parent=x, factor=2)
           u1 = TimeFunction(name='u1', dimensions=(x1,), size=grid.shape[0]/factor)
           # The generated code will look like
           float (*restrict u1)[x1_size + 1] =

    .. note::

        Sometimes the ConditionalDimension itself, rather than its parent, needs
        to be used to index into an array. For example, this may happen when an
        array is indirectly addressed and the ConditionalDimension's parent
        doesn't define an affine iteration space. In such a case, one should
        create the ConditionalDimension with the flag ``indirect=True``.
    """

    def __new__(cls, name, parent, factor=None, condition=None, indirect=False):
        return ConditionalDimension.__xnew_cached_(cls, name, parent, factor,
                                                   condition, indirect)

    def __new_stage2__(cls, name, parent, factor, condition, indirect):
        newobj = DerivedDimension.__xnew__(cls, name, parent)
        newobj._factor = factor
        newobj._condition = condition
        newobj._indirect = indirect
        return newobj

    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    @property
    def spacing(self):
        return self.factor * self.parent.spacing

    @property
    def factor(self):
        return self._factor if self._factor is not None else 1

    @property
    def condition(self):
        return self._condition

    @property
    def indirect(self):
        return self._indirect

    @property
    def index(self):
        return self if self.indirect is True else self.parent

    @property
    def _properties(self):
        return (self._factor, self._condition, self._indirect)

    # Pickling support
    _pickle_kwargs = DerivedDimension._pickle_kwargs + ['factor', 'condition', 'indirect']