Reputation: 5551
I have a sympy sum:
import sympy
x = sympy.IndexedBase('x')
n = sympy.symbols('n')
i = sympy.symbols('i', cls=sympy.Idx)
my_sum = sympy.summation(x[i], (i,1,n))
In my various calculations and whatnot, I sometimes end up with nested sums. Sometimes, these sums have "free variables", and sometimes not. For example, I might end up with the following:
my_double_sum = sympy.summation(my_sum, (i,1,n))
Now, since my_sum
doesn't have a "free" i
from the persepective of the outer sum, then this should simplify to:
n*Sum(x[i], (i, 1, n))
However, sympy.simplify(my_double_sum)
gives:
Sum(x[i], (i, 1, n), (i, 1, n))
How can I make sympy
simplify summations intelligently with respect to the free summand indices?
Upvotes: 1
Views: 126
Reputation: 16184
This behavior did look a bit weird. Here is why it happens (and see the bottom for what is a bug and what is not).
First, summation
is just a syntactic sugar to creating a Sum
and running .doit()
. Sum
's doit
uses among the rest eval_sum
, which extracts the limit bounds when the limit variable is not a free variable of the function, and a simple checks shows that indeed this should hold here! (and as you showed, it doesn't):
>>> i in my_sum.free_symbols
False
so I did a little digging into the summation
module.
Now, Sum
has a parent called AddWithLimits
. In its class creator, it uses the _common_new
function that denests the function it receives.
This turns Sum(Sum(x[i], (i, 1, n)), (i, 1, n))
into Sum(x[i], (i, 1, n), (i, 1, n))
(way before doit
is invoked), so the internal function is x[i]
and not the Sum
object you defined in my_sum
(which doesn't pop up at first sight), so the limit variable actually is a free variable of the function.
I tried to manually cancel the denesting, commenting the three lines under the comment # denest any nested calls
, and indeed, I received
n*Sum(x[i], (i, 1, n))
Of course, simply changing that would probably hurt other parts of the code, since the denesting is assumed in many other ExprWithLimits
functions. Whether this is an intended behvior can be argued in favor, but if you think it should be covered, it probably has to be specified explicitly inside eval_sum
as a special case.
However, I would expect summation with another variable to be simplified normally, like
summation(x[i], (i, 1, n), (j, 1, n))
which does not. This, I suspect, is more of an intended behavior (which is caused because the first iteration over eval_sum
returns None
, so it skips the j
symbol expansion).
Upvotes: 1