Him
Him

Reputation: 5551

Sympy simplifying sums based on free parameters

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

Answers (1)

Uriel
Uriel

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

Related Questions