Repository: sympy/sympy

Issue:
Idx instances can't be used as limits in a Sum
```
In [15]: import sympy as sm

In [16]: i = sm.symbols('i', cls=sm.Idx)

In [17]: sm.Sum(sm.Indexed('r', i), (i, 1, 3))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-17-76d0a23c5a34> in <module>()
----> 1 sm.Sum(sm.Indexed('r', i), (i, 1, 3))

/home/moorepants/anaconda/lib/python2.7/site-packages/sympy/concrete/summations.pyc in __new__(cls, function, *symbols, **assumptions)
    138 
    139     def __new__(cls, function, *symbols, **assumptions):
--> 140         obj = AddWithLimits.__new__(cls, function, *symbols, **assumptions)
    141         if not hasattr(obj, 'limits'):
    142             return obj

/home/moorepants/anaconda/lib/python2.7/site-packages/sympy/concrete/expr_with_limits.pyc in __new__(cls, function, *symbols, **assumptions)
    351 
    352         if symbols:
--> 353             limits, orientation = _process_limits(*symbols)
    354         else:
    355             # symbol not provided -- we can still try to compute a general form

/home/moorepants/anaconda/lib/python2.7/site-packages/sympy/concrete/expr_with_limits.pyc in _process_limits(*symbols)
     55                     continue
     56 
---> 57         raise ValueError('Invalid limits given: %s' % str(symbols))
     58 
     59     return limits, orientation

ValueError: Invalid limits given: ((i, 1, 3),)

In [18]: i = sm.symbols('i', integer=True)

In [19]: sm.Sum(sm.Indexed('r', i), (i, 1, 3))
Out[19]: Sum(r[i], (i, 1, 3))
```

Discussion:
leosartaj: Ping @moorepants @jcrist 
moorepants: Looks good here, thanks. Please address my two comments.
moorepants: +1 once tests pass.
leosartaj: There were failures, in codegen probably due to ``free_symbols`` attribute I added in ``Indexed``. This was added to allow evaluation of ``Sum``. 

Something like this happened without ``free_symbols``

```
>>> i = symbols('i', cls=Idx)
>>> r = Indexed('r', i)
>>> Sum(r, (i, 1, 3)).doit()
3*r[i]
```

This was probably due to [L-547](https://github.com/leosartaj/sympy/blob/idx/sympy/concrete/summations.py#L547) in summations.py

>  if i not in f.free_symbols:

Here ``i ``is an instance of ``Idx`` while ``free_symbols`` contains ``i`` but that is an instance of ``Symbol``. This is due to ``Idx`` internally storing it is an integral Symbol.

So to overcome this, I used the internal integral ``Symbol`` to evaluate ``Sum``.

Please have a look @moorepants.
leosartaj: Ping @moorepants @jcrist 

Patch:
diff --git a/sympy/concrete/expr_with_limits.py b/sympy/concrete/expr_with_limits.py
index ef84e11193..34b8a2aeb1 100644
--- a/sympy/concrete/expr_with_limits.py
+++ b/sympy/concrete/expr_with_limits.py
@@ -14,6 +14,7 @@
 from sympy.utilities import flatten
 from sympy.utilities.iterables import sift
 from sympy.matrices import Matrix
+from sympy.tensor.indexed import Idx
 
 
 def _process_limits(*symbols):
@@ -25,12 +26,12 @@ def _process_limits(*symbols):
     limits = []
     orientation = 1
     for V in symbols:
-        if isinstance(V, Symbol):
+        if isinstance(V, (Symbol, Idx)):
             limits.append(Tuple(V))
             continue
         elif is_sequence(V, Tuple):
             V = sympify(flatten(V))
-            if V[0].is_Symbol:
+            if isinstance(V[0], (Symbol, Idx)):
                 newsymbol = V[0]
                 if len(V) == 2 and isinstance(V[1], Interval):
                     V[1:] = [V[1].start, V[1].end]
diff --git a/sympy/concrete/summations.py b/sympy/concrete/summations.py
index 3513950223..c2a8f5bb84 100644
--- a/sympy/concrete/summations.py
+++ b/sympy/concrete/summations.py
@@ -11,6 +11,7 @@
 from sympy.polys import apart, PolynomialError
 from sympy.solvers import solve
 from sympy.core.compatibility import range
+from sympy.tensor.indexed import Idx
 
 
 class Sum(AddWithLimits, ExprWithIntLimits):
@@ -176,6 +177,8 @@ def doit(self, **hints):
             if dif.is_integer and (dif < 0) == True:
                 a, b = b + 1, a - 1
                 f = -f
+            if isinstance(i, Idx):
+                i = i.label
 
             newf = eval_sum(f, (i, a, b))
             if newf is None:
diff --git a/sympy/series/sequences.py b/sympy/series/sequences.py
index 52ff93d804..82ddcc6f63 100644
--- a/sympy/series/sequences.py
+++ b/sympy/series/sequences.py
@@ -14,6 +14,7 @@
 from sympy.polys import lcm
 from sympy.sets.sets import Interval, Intersection
 from sympy.utilities.iterables import flatten
+from sympy.tensor.indexed import Idx
 
 
 ###############################################################################
@@ -92,11 +93,8 @@ def free_symbols(self):
         >>> SeqFormula(m*n**2, (n, 0, 5)).free_symbols
         set([m])
         """
-        fsyms = set().union(*[a.free_symbols for a in self.args])
-        for d in self.variables:
-            if d in fsyms:
-                fsyms.remove(d)
-        return fsyms
+        return (set(j for i in self.args for j in i.free_symbols
+                   .difference(self.variables)))
 
     @cacheit
     def coeff(self, pt):
@@ -452,7 +450,7 @@ def _find_x(periodical):
                 x = _find_x(periodical)
                 start, stop = limits
 
-        if not isinstance(x, Symbol) or start is None or stop is None:
+        if not isinstance(x, (Symbol, Idx)) or start is None or stop is None:
             raise ValueError('Invalid limits given: %s' % str(limits))
 
         if start is S.NegativeInfinity and stop is S.Infinity:
@@ -594,7 +592,7 @@ def _find_x(formula):
                 x = _find_x(formula)
                 start, stop = limits
 
-        if not isinstance(x, Symbol) or start is None or stop is None:
+        if not isinstance(x, (Symbol, Idx)) or start is None or stop is None:
             raise ValueError('Invalid limits given: %s' % str(limits))
 
         if start is S.NegativeInfinity and stop is S.Infinity:
diff --git a/sympy/tensor/indexed.py b/sympy/tensor/indexed.py
index fad892a2ab..477b654dad 100644
--- a/sympy/tensor/indexed.py
+++ b/sympy/tensor/indexed.py
@@ -134,7 +134,7 @@ class Indexed(Expr):
     """
     is_commutative = True
 
-    def __new__(cls, base, *args, **kw_args):
+    def __new__(cls, base, *args):
         from sympy.utilities.misc import filldedent
 
         if not args:
@@ -145,7 +145,7 @@ def __new__(cls, base, *args, **kw_args):
             raise TypeError(filldedent("""
                 Indexed expects string, Symbol or IndexedBase as base."""))
         args = list(map(sympify, args))
-        return Expr.__new__(cls, base, *args, **kw_args)
+        return Expr.__new__(cls, base, *args)
 
     @property
     def base(self):


Test patch:
diff --git a/sympy/concrete/tests/test_sums_products.py b/sympy/concrete/tests/test_sums_products.py
index 4b0c7ba891..55eecda7b1 100644
--- a/sympy/concrete/tests/test_sums_products.py
+++ b/sympy/concrete/tests/test_sums_products.py
@@ -2,8 +2,8 @@
     Abs, And, binomial, Catalan, cos, Derivative, E, Eq, exp, EulerGamma,
     factorial, Function, harmonic, I, Integral, KroneckerDelta, log,
     nan, Ne, Or, oo, pi, Piecewise, Product, product, Rational, S, simplify,
-    sqrt, Sum, summation, Symbol, symbols, sympify, zeta, gamma, Le
-)
+    sqrt, Sum, summation, Symbol, symbols, sympify, zeta, gamma, Le, Indexed,
+    Idx)
 from sympy.abc import a, b, c, d, f, k, m, x, y, z
 from sympy.concrete.summations import telescopic
 from sympy.utilities.pytest import XFAIL, raises
@@ -831,3 +831,9 @@ def test_issue_4668():
 def test_matrix_sum():
     A = Matrix([[0,1],[n,0]])
     assert Sum(A,(n,0,3)).doit() == Matrix([[0, 4], [6, 0]])
+
+
+def test_issue_9594():
+    i = symbols('i', cls=Idx)
+    r = Indexed('r', i)
+    Sum(r, (i, 0, 3)).doit() == [r.subs(i, j) for j in range(4)]
diff --git a/sympy/series/tests/test_sequences.py b/sympy/series/tests/test_sequences.py
index be6bdbb014..2c42ccc9c5 100644
--- a/sympy/series/tests/test_sequences.py
+++ b/sympy/series/tests/test_sequences.py
@@ -1,5 +1,5 @@
 from sympy import (S, Tuple, symbols, Interval, EmptySequence, oo, SeqPer,
-                   SeqFormula, sequence, SeqAdd, SeqMul)
+                   SeqFormula, sequence, SeqAdd, SeqMul, Indexed, Idx)
 from sympy.series.sequences import SeqExpr, SeqExprOp
 from sympy.utilities.pytest import raises
 
@@ -246,3 +246,11 @@ def test_operations():
 
     assert form.coeff_mul(m) == SeqFormula(m*n**2, (n, 0, oo))
     assert per.coeff_mul(m) == SeqPer((m, 2*m), (n, 0, oo))
+
+
+def test_Idx_limits():
+    i = symbols('i', cls=Idx)
+    r = Indexed('r', i)
+
+    assert SeqFormula(r, (i, 0, 5))[:] == [r.subs(i, j) for j in range(6)]
+    assert SeqPer((1, 2), (i, 0, 5))[:] == [1, 2, 1, 2, 1, 2]
diff --git a/sympy/tensor/tests/test_indexed.py b/sympy/tensor/tests/test_indexed.py
index 370bd501bd..aac1bb5370 100644
--- a/sympy/tensor/tests/test_indexed.py
+++ b/sympy/tensor/tests/test_indexed.py
@@ -177,6 +177,7 @@ def test_complex_indices():
     assert A.rank == 2
     assert A.indices == (i, i + j)
 
+
 def test_not_interable():
     i, j = symbols('i j', integer=True)
     A = Indexed('A', i, i + j)