<commit_before>"""Module that defines indexed objects with arbitrary transformation properties.

    The Idx class represent indices and each Idx can optionally contain
    information about its range.  Transformation properties can be defined in
    subclasses.

    Examples
    ========

    To express a matrix-vector product in terms of Indexed objects:


    >>> from sympy.tensor import Indexed, Idx
    >>> from sympy import symbols, Eq
    >>> i,j,n,m,M,x,y = symbols('i j n m M x y')
    >>> M = Indexed(M, Idx(i, m), Idx(j, n))
    >>> x = Indexed(x, Idx(j, n))
    >>> y = Indexed(y, Idx(i, m))
    >>> Eq(y, M*x)
    y(i) == M(i, j)*x(j)

"""

from sympy import Expr, Basic, SymTuple, Symbol, Integer, sympify, S


class Indexed(Expr):
    """Represent an arbitrary object with indices, e.g. a symbolic array element.

    >>> from sympy.tensor import Indexed
    >>> from sympy.abc import a, x, y
    >>> Indexed(a, x, y)
    a(x, y)

    """

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

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

    @property
    def rank(self):
        """returns the number of indices"""
        return len(args)-1

    @property
    def dimensions(self):
        return [(i.lower, i.upper) for i in self.indices]

    def _sympystr(self, p):
        indices = map(p.doprint, self.indices)
        return "%s(%s)" % (p.doprint(self.label), ", ".join(indices))


class Idx(Basic):
    """Represents an index, either symbolic or integer.

    Optionally you can specify a range [default=0]
    .. Symbol, integer  --  interpreted as dimension, lower and upper ranges are
                            set to 0 and range-1
    .. tuple  --  interpreted as lower, upper elements in range.

    Note that the Idx constructor is rather pedantic, and will not accept
    non-integer symbols.  The only exception is that you can use oo and -oo to
    specify an unbounded range.  For all other cases, both label and bounds
    must be declared as integers, in the sense that for a symbol n,
    n.is_integer must return True.

    >>> from sympy.tensor import Indexed, Idx
    >>> from sympy import symbols, oo
    >>> n, i, L, U = symbols('n i L U', integer=True)

    1) Both upper and lower bound specified<commit_msg>Indexed: Added option to allow creation of tensor stem before attaching indices<commit_after>"""Module that defines indexed objects with arbitrary transformation properties.

    The Idx class represent indices and each Idx can optionally contain
    information about its range.  Transformation properties can be defined in
    subclasses.

    Examples
    ========

    To express a matrix-vector product in terms of Indexed objects:


    >>> from sympy.tensor import Indexed, Idx
    >>> from sympy import symbols, Eq
    >>> i,j,n,m,M,x,y = symbols('i j n m M x y')
    >>> M = Indexed(M, Idx(i, m), Idx(j, n))
    >>> x = Indexed(x, Idx(j, n))
    >>> y = Indexed(y, Idx(i, m))
    >>> Eq(y, M*x)
    y(i) == M(i, j)*x(j)

"""

from sympy import Expr, Basic, SymTuple, Symbol, Integer, sympify, S

class IndexException(Exception):
    pass

class Indexed(Expr):
    """Represent an arbitrary object with indices, e.g. a symbolic array element.

    >>> from sympy.tensor import Indexed
    >>> from sympy import symbols
    >>> i, j, k = symbols('i j k', integer=True)
    >>> a = symbols('a')
    >>> Indexed(a, i, j)
    a(i, j)

    There is also syntactic sugar that allows declaration of the stem independent
    of the indices:

    >>> A = Indexed(a); A
    a
    >>> A(i, j, k)
    a(i, j, k)

    """

    def __call__(self, *indices, **kw_args):
        if self.rank != 0:
            raise IndexException("Indexed with rank > 0 cannot be used as stem")
        return self.func(self.label, *indices, **kw_args)

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

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

    @property
    def rank(self):
        """returns the number of indices"""
        return len(args)-1

    @property
    def dimensions(self):
        return [(i.lower, i.upper) for i in self.indices]

    def _sympystr(self, p):
        indices = map(p.doprint, self.indices)
        if indices:
            return "%s(%s)" % (p.doprint(self.label), ", ".join(indices))
        else:
            return "%s" % p.doprint(self.label)


class Idx(Basic):
    """Represents an index, either symbolic or integer.

    Optionally you can specify a range [default=0]
    .. Symbol, integer  --  interpreted as dimension, lower and upper ranges are
                            set to 0 and range-1
    .. tuple  --  interpreted as lower, upper elements in range.

    Note that the Idx constructor is rather pedantic, and will not accept
    non-integer symbols.  The only exception is that you can use oo and -oo to
    specify an unbounded range.  For all other cases, both label and bounds
    must be declared as integers, in the sense that for a symbol n,
    n.is_integer must return True.

    >>> from sympy.tensor import Indexed, Idx
    >>> from sympy import symbols, oo
    >>> n, i, L, U = symbols('n i L U', integer=True)

    1) Both upper and lower bound specified
{'fraction_non_alphanumeric': 0.1148105625717566, 'fraction_numerical': 0.0030616150019135095, 'mean_word_length': 2.6273421235253296, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 94, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7426008', 'n_tokens_mistral': 1638, 'n_tokens_neox': 1571, 'n_words': 627}